Experimental Studies | JOBS II

library(haven)
library(MCMCpack)
library(mvtnorm)

# clean workspace
rm(list=ls()) 

# set working directory in RStudio to directory where currently active script
setwd(dirname(rstudioapi::getActiveDocumentContext()$path))

1. Load dataset

# import file
df_jobs <- read_dta('JOBSII_HR.dta')
df_jobs

Only 54% of individuals who were assigned to the intervention actually received the treatment. In the literature, this problem is known as noncompliance. Here we ignore noncompliance issues focusing on assessing causal effects of the assignment to the treatment (Intention-to-treat analysis).

So, I drop the W variable and focus on the assignment variable Z for assessing the causal effects of the treatment to ensures that analysis aligns with the intention-to-treat principle.

# Drop the W variable
df_jobs <- df_jobs[, !(names(df_jobs) == "W")]
# df_jobs

### Extra
table(df_jobs$Z)

  0   1 
130 268 
prop.table(table(df_jobs$Z))

        0         1 
0.3266332 0.6733668 
# table(df_jobs$Z, df_jobs$motivation)
# summary(df_jobs$motivation)
# unique(df_jobs$motivation)

Visualization

# Outcome variables
outcome_vars <- c('depress6', 'employ6')

par(mfrow=c(1, 2))

for (var in outcome_vars) {
  hist(df_jobs[[var]], freq=FALSE, main=paste(var,"| post treatment"), xlab=NULL, col="#FC4E07")}

#
outcome_vars <- c('depress0', 'depress6')

par(mfrow=c(2, 2))


for (var in outcome_vars) {hist(df_jobs[[var]][df_jobs$Z == 0], freq=FALSE, main=paste(var, "Control", sep=" | "), xlab=NULL, col="#FC4E07")}
for (var in outcome_vars) {hist(df_jobs[[var]][df_jobs$Z == 1], freq=FALSE, main=paste(var, "Treat", sep=" | "), xlab=NULL, col="#00AFBB")}

#
outcome_vars <- c('depress0', 'depress6')

par(mfrow=c(1, 2))


for (var in outcome_vars) {
  hist(df_jobs[[var]], freq=FALSE, main=var, xlab=NULL, col="#FC4E07")}


# Binary variables
bin_vars <- c('sex', 'race', 'nonmarried') 

par(mfrow=c(length(bin_vars), 3))



for (var in bin_vars) {
  
  hist(df_jobs[[var]][df_jobs$Z == 0], xlab=NULL, main=paste(var, "Control", sep=" | "), col="#FC4E07")
  
  hist(df_jobs[[var]][df_jobs$Z == 1], xlab=NULL, main=paste(var, "Treat", sep=" | "), col="#00AFBB")
  
  hist(df_jobs[[var]], freq=FALSE, main=paste(var), xlab=NULL, col="green")
  }


# Pre-treatment continuous variables
cont_pre_vars <- c('age', 'educ', 'EconHard', 'assertive', 'motivation', 'depress0')

# Pre-treatment continuous variables
rows <- 1.5
par(mfrow=c(2*rows, length(cont_pre_vars)/rows))


for (var in cont_pre_vars) {
  hist(df_jobs[[var]][df_jobs$Z == 0], xlab=NULL, main=paste(var, "Control", sep=" | "), col="#FC4E07")
  
  hist(df_jobs[[var]][df_jobs$Z == 1], xlab=NULL, main=paste(var, "Treat", sep=" | "), col="#00AFBB")
  }


# Reset par
par(mfrow=c(1,1))

Looking at covariates it seems to be a similar distribution among the assignment to Control or Treatment. Considering depress0 there is an higher density for the low values in treatment group.

Looking at depress before and after treatment, for both control and treatment groups there is a shift to the lower levels of depression, showing the possibility that some other factors helped people, not only treatment in our study.

2. For each variable, calculate the mean for the whole sample and within each treatment group.

For continuous covariates, also report the medians, standard deviation and ranges within each treatment group. Record your results in a table. In a few sentences, comment on what you see and whether it is expected.

# header <- colnames(df_jobs)

all_var <- c("sex", "age", "race", "nonmarried", "educ", "EconHard", "assertive", "motivation", "depress0", "Z", "employ6", "depress6")

# Mean, sd, median, range by treatment of continuous vars
continuous_var <- c('age', 'educ', 'EconHard', 'assertive', 'motivation', 'depress0', 'depress6')


# Descriptive statistics
All_stat<-data.frame( cbind(
      apply(df_jobs[,all_var], 2,mean), 
      
      apply(df_jobs[df_jobs$Z==0,all_var], 2,mean),
      
      apply(df_jobs[df_jobs$Z==1,all_var], 2,mean)))

colnames(All_stat)<- c("Mean", " Mean-C", " Mean-T")
round(All_stat,2)

Cont_stat<-data.frame( cbind(
      apply(df_jobs[df_jobs$Z==0,continuous_var], 2, median),
      
      apply(df_jobs[df_jobs$Z==1,continuous_var], 2, median),
      
      apply(df_jobs[df_jobs$Z==0,continuous_var], 2, sd),
      
      apply(df_jobs[df_jobs$Z==1,continuous_var], 2, sd),
      
      apply(df_jobs[df_jobs$Z==0,continuous_var], 2, min),
      
      apply(df_jobs[df_jobs$Z==1,continuous_var], 2, min),
      
      apply(df_jobs[df_jobs$Z==0,continuous_var], 2, max),
      
      apply(df_jobs[df_jobs$Z==1,continuous_var], 2, max)))

colnames(Cont_stat)<- c(" Median-C", " Median-T", " s.d.-C", " s.d.-T", " Min-C", " Min-T", " Max-C", " Max-T")
round(Cont_stat,2)

Stat <- merge(All_stat, Cont_stat, by="row.names", all = TRUE)
Stat

In terms of randomization, considering for example the variable “age”, the mean value and standard deviation are quite the same for treated and non-treated groups, but it has a little significant difference between max age, 69 vs 61. Also by sex, there’s a 7 % discard between the two groups, along with other not-so-similar distributions like race, marital status. Could this maybe affect the results?

The depression level looks a little lower in the treatment group, that could be taken into account considering the outcome “depress6”. In both cases there is a drop down in the mean value of depression from pre to post treatment time.

In terms of employment at the end of the 6 months, there’s a difference in terms of mean value, but I have no pre-treatment values to do any consideration about. Speculating with Economic Hardness it seems to underline the effect of the treatment in employment status.

3. Fisher exact p−value approach. Outcome variable: “depression six months after the intervention assignment.”

N  <- nrow(df_jobs)        # total sample size
Nt <- sum(df_jobs$Z==1)    # number of treated units
Nc <- sum(df_jobs$Z==0)    # number of controls

c(N, Nt, Nc)
[1] 398 268 130
##--------------------------------------------#
## Fisher's Exact p-value
##--------------------------------------------#

## Sharp null hypothesis that the treatment had no effect 
## H0: Yi(1)=Yi(0) for i=1,...,N

## Considering outcome variable: “depression six months after the intervention assignment”


# possible combinations
nass <- choose(N,Nt) # number of assignment vectors
nass # we should compute the average treatment effect for each of them...
[1] 6.763821e+107

(a) Approximate using 5000 draws from the randomization distribution, the exact Fisher p−values for a sharp null hypothesis of zero treatment effects using the following two statistics:

Absolute value of the difference in average outcomes by treatment status Absolute value of the difference in average ranks by treatment status.

# Absolute value of the difference in average outcomes by treatment status:
dif.ave.obs <- mean(df_jobs$depress6[df_jobs$Z==1]) - mean(df_jobs$depress6[df_jobs$Z==0])
dif.ave.obs
[1] -0.1360192
Tobs.dif.ave  <- abs(dif.ave.obs)
Tobs.dif.ave
[1] 0.1360192
r <- rank(df_jobs$depress6, ties.method = "average")

dif.obs.r <- mean(r[df_jobs$Z==1]) - mean(r[df_jobs$Z==0])
dif.obs.r
[1] -21.53929
Tobs.dif.r <- abs(dif.obs.r)
Tobs.dif.r
[1] 21.53929
# We test the Sharp null against - so potential outcomes with or without treatment are the same, so we can construct them

# fix a seed for reproducibility
set.seed(23)

# P-values estimated using K draws from the randomization distribution:
K <- 5000 

p.ave <- p.r <- 0  # P.value
Tdif.ave.dist <- Tdif.dist.r <- NULL  # initializing vectors


# at every iteration sample a N dimention vector in q
for(k in 1:K){
  Z.sim <- sample(df_jobs$Z, N, replace=FALSE)  # simulation, doesn't metter if with replacement or not
  
  dif.ave <- mean(df_jobs$depress6[Z.sim==1]) - mean(df_jobs$depress6[Z.sim==0]) # mean diff
  Tdif.ave <- abs(dif.ave)  # abs value
  Tdif.ave.dist <- c(Tdif.ave.dist, Tdif.ave)  # Appends Tdif.ave to Tdif.ave.dist to keep track of distribution under null hypothesis
  p.ave <- p.ave + 1*(Tdif.ave>=Tobs.dif.ave)  # Updates a counter if observed mean difference is greater than to simulated mean differences
  
  # Rank
  dif.r <- mean(r[Z.sim==1]) - mean(r[Z.sim==0])
  Tdif.r <- abs(dif.r)
  Tdif.dist.r <- c(Tdif.dist.r, Tdif.r)
  p.r <- p.r + 1*(Tdif.r >= Tobs.dif.r)
  }

p.ave <- p.ave/K
p.r   <- p.r/K

c(Tobs.dif.ave, Tobs.dif.r, p.ave, p.r)
[1]  0.1360192 21.5392939  0.0900000  0.0756000

Under the null hypothesis of no effect of the program, having a p-value of 0.09 and 0.08 make no sufficient evidence to strongly reject the null hypothesis.

par(mfrow=c(1,2))

hist(Tdif.ave.dist, freq=TRUE, main="Abs diff in average outcomes",
     breaks=100,
     xlab=NULL)

abline(v=Tobs.dif.ave, col="forestgreen",lwd=2) 


hist(Tdif.dist.r, freq=TRUE, main="Abs diff in average ranks",
     breaks=100,
     xlab=NULL)

abline(v=Tobs.dif.r, col="forestgreen",lwd=2) 

(b) Calculate a 90% Fisher interval for a constant additive treatment effect using the absolute value of the difference in average out- comes by treatment status as test statistic.

### Interval estimates based on FEP (simulated p-values)
# Fisher interval for a common additive effect
# H0: Y(1) = Y(0) + tau

tau <- seq(-.5, .5, by=.05)
p.dif <- rep(0, length(tau))
Tobs.dif <- NULL


for(k in 1:K){
  Z.sim <- sample(df_jobs$Z, N, replace=FALSE)
  
  for(j in 1:length(tau)){
    
    #Imputed df_jobs under the null hypothesis
    Y0 <- df_jobs$depress6*(df_jobs$Z==0) + (df_jobs$depress6-tau[j])*(df_jobs$Z==1)
    Y1 <- df_jobs$depress6*(df_jobs$Z==1) + (df_jobs$depress6+tau[j])*(df_jobs$Z==0)
    
    Tobs.dif[j] <- abs(mean(df_jobs$depress6[df_jobs$Z==1]) - mean(df_jobs$depress6[df_jobs$Z==0]) - tau[j])
    
    Tdif <- abs(mean(Y1[Z.sim==1]) - mean(Y0[Z.sim==0]) - tau[j])
    
    p.dif[j]<- p.dif[j] + 1 * (Tdif>=Tobs.dif[j])
    }
    }

p.dif<- p.dif/K

FCI<-cbind(tau, Tobs.dif, p.dif)
FCI
        tau  Tobs.dif  p.dif
 [1,] -0.50 0.3639808 0.0000
 [2,] -0.45 0.3139808 0.0000
 [3,] -0.40 0.2639808 0.0012
 [4,] -0.35 0.2139808 0.0086
 [5,] -0.30 0.1639808 0.0438
 [6,] -0.25 0.1139808 0.1464
 [7,] -0.20 0.0639808 0.4164
 [8,] -0.15 0.0139808 0.8670
 [9,] -0.10 0.0360192 0.6568
[10,] -0.05 0.0860192 0.2756
[11,]  0.00 0.1360192 0.0880
[12,]  0.05 0.1860192 0.0204
[13,]  0.10 0.2360192 0.0034
[14,]  0.15 0.2860192 0.0006
[15,]  0.20 0.3360192 0.0000
[16,]  0.25 0.3860192 0.0000
[17,]  0.30 0.4360192 0.0000
[18,]  0.35 0.4860192 0.0000
[19,]  0.40 0.5360192 0.0000
[20,]  0.45 0.5860192 0.0000
[21,]  0.50 0.6360192 0.0000
# I will select p-values greater than 0.1, to find a confidence interval, since I want a 90% Fisher interval for a constant additive treatment, I will take the interval where values are greater than 0.1. so:
# -0.2, -0.1 or -0.25, -0.05 with -

# 

#For taus between .6 and 3 we cannot reject the the null hypothesis
#H0: Y(1) - Y(0) = tau, i.e., the observed test statistic is not so extreme to 
#reject the hypothesis of a treatment effect equal to tau. E.g., we cannot 
#reject the hypothesis of a trt effect equal to 3, but we do reject the 
#hypothesis of trt effect equal to 3.1 since the relative p-value is <0.05.

Confidence interval: I seek a 90% Fisher interval for a constant additive treatment. Considering the test statistics performed, it’s not so extreme to rejected the null hypothesis in the interval where p-values are greater than 0.1, so the corresponding taus range is [-0.25, -0.05].

4. Neyman’s Repeated Sampling Approach. Outcome variable: “depression six months after the intervention assignment.”

(a) Calculate an unbiased estimate of the average treatment effect.

##--------------------------------------------#
## Neyman's Repeated Sampling Approach
##--------------------------------------------#

Yobs <- df_jobs$depress6
Z    <- df_jobs$Z

tau.dif<- mean(Yobs[Z==1]) - mean(Yobs[Z==0])
tau.dif
[1] -0.1360192

Avarage treatment effect.

# Estimates of the sample variance of the potential control outcome
s2.c<- var(Yobs[Z==0])
s2.c
[1] 0.6196765
# Estimates of the sample variance of the potential treated outcome
s2.t<- var(Yobs[Z==1])
s2.t
[1] 0.5485401
## Estimates of the variance of the treatment effect estimators
Vneyman <- s2.c/Nc + s2.t/Nt
Vneyman
[1] 0.006813534
  1. Apply Neyman’s method to construct a 90% large sample confidence interval for the average treatment effect
## Confidence intervals: 
#  1-alpha = 0.9, so 1 - (alpha/2) = 0.95
X <- qnorm(0.95)

c(tau.dif - X * sqrt(Vneyman), tau.dif + X * sqrt(Vneyman))
[1] -0.2717922196 -0.0002461851

The confidence interval found is similar to the one computed by Fisher (above).

The negative values in the interval indicate that there is a statistically significant decrease in the outcome variable associated with the treatment based on Neyman’s method.

## Testing
## Hypotheses:  
#  H0: tau.dif  = 0 vs 
#  H1: tau.dif != 0
tneyman <- (tau.dif-0)/sqrt(Vneyman)  # test statistic for the Neyman test
tneyman
[1] -1.647836
# p-value based on the normal approximation
2*(1-pnorm(abs(tneyman)))  # gives the cumulative distribution function (CDF) of the standard normal distribution
[1] 0.09938631

Given a p-value of 0.09938631, it suggests that the observed estimate is not significantly different from zero at a conventional significance level of 0.05. However, we’re interested in constructing a 90% confidence interval, the fact that the interval does not include zero would suggest that we might slightly reject the null hypothesis in favor of alternative one of non-zero-effect.

5. Bayesian model-based analysis. Bayesian model-based analysis for the outcome variable “depression six months after the intervention assignment.” Assume that Yi(0) and Yi(1) are independent and are both log-normally distributed.

Derive the posterior distributions of the finite sample average causal effect and the super-population average causal effect. Plot the resulting posterior distributions in a histogram and report the following summary statistics of the resulting posterior distributions: mean, standard deviation, median, 2.5% and 97.5% percentiles.

mcmc.m5 <- function(niter, nburn, thin=1,   
                    par.prior,
                    Outcome.obs, W,
                    seed=NULL, theta.start=NULL, cred.level=0.95){ 
  
    Yobs <- log(Outcome.obs)  # logarithm transformation
    
    Nc<- sum(1-W); Nt<- sum(W); N<- Nt+Nc
    yobs.c<-mean(Yobs[W==0]); yobs.t<-mean(Yobs[W==1])
    
    draws<- seq((nburn+1), niter, by=thin)
    ndraws<- length(draws)
    j <- 0 # Counter j=1...ndraws
    
    # Start values
    if(is.null(theta.start)==TRUE){

    theta <- list(beta.c  = mean(Yobs[W==0]) + rnorm(1,0, 0.1),
                  beta.t  = mean(Yobs[W==1]) + rnorm(1,0, 0.1),
                  sigma2.c = var(Yobs[W==0]) + rnorm(1,0, 0.01),
                  sigma2.t = var(Yobs[W==1]) + rnorm(1,0, 0.01)
                  )

    }else{theta<- theta.start}
  
  
    Theta <- matrix(NA, ndraws,  length(unlist(theta)) )
    colnames(Theta) <- names(theta)
    
    Estimands<- matrix(NA, ndraws, 2)
    colnames(Estimands)<- c("ate.fs", "ate.sp")
    
    if(is.null(seed)==FALSE){
      set.seed(seed)}

    for(l in 1:niter){
    
    ##Update beta.c
    tau2.c.obs   <- 1/{Nc/theta$sigma2.c + 1/par.prior$tau2.c}
    nu.c.obs     <- tau2.c.obs*{(yobs.c*Nc)/theta$sigma2.c + par.prior$nu.c/par.prior$tau2.c}
    theta$beta.c <- rnorm(1, nu.c.obs, sqrt(tau2.c.obs))
    
    ##Update beta.t
    tau2.t.obs   <- 1/{Nt/theta$sigma2.t+ 1/par.prior$tau2.t}
    nu.t.obs     <- tau2.t.obs*{(yobs.t*Nt)/theta$sigma2.t + par.prior$nu.t/par.prior$tau2.t}
    theta$beta.t <- rnorm(1, nu.t.obs, sqrt(tau2.t.obs))
    
    ##Update sigma2.c
    a.c.obs        <- Nc + par.prior$a.c
    b2.c.obs       <-  {par.prior$a.c*par.prior$b2.c + sum({Yobs[W==0]-theta$beta.c}^2)}/a.c.obs
    theta$sigma2.c <- {a.c.obs*b2.c.obs}/rchisq(1, a.c.obs)
    
    ##Update sigma2.t
    a.t.obs        <- Nt + par.prior$a.t
    b2.t.obs       <-  {par.prior$a.t*par.prior$b2.t + sum({Yobs[W==1]-theta$beta.t}^2)}/a.t.obs
    theta$sigma2.t <-  {a.t.obs*b2.t.obs}/rchisq(1, a.t.obs)      
    
    rm(tau2.c.obs, nu.c.obs,tau2.t.obs, nu.t.obs, a.c.obs, b2.c.obs, a.t.obs, b2.t.obs)
    
    if(sum(l == draws)==1){
      j <- j+1
      
      Theta[j,]<- unlist(theta)
      
      # Imputate the missing potential outcomes using Ymis | Yobs, W, X, theta
      Y0<-Y1<-NULL
      
      Y0[W==0]<- Outcome.obs[W==0] # values with no log transf
      Y0[W==1]<- exp(rnorm(Nt, theta$beta.c, sqrt(theta$sigma2.c))) # inverse transf
      
      Y1[W==0]<- exp(rnorm(Nc, theta$beta.t, sqrt(theta$sigma2.t))) # inverse transf
      Y1[W==1]<- Outcome.obs[W==1] # values with no log transf
      
      Estimands[j,"ate.fs"] <- mean(Y1) - mean(Y0)
      Estimands[j,"ate.sp"] <- exp(theta$beta.t + 0.5*theta$sigma2.t)-exp(theta$beta.c +0.5*theta$sigma2.c) # HINT
    }
  }
  
  # Sim posterior distrib of ATE.FS and ATE.SP
  probs<-c((1-cred.level)/2,1-(1-cred.level)/2)  # hint
  
  est<-round(cbind(apply(Estimands,2,mean),
                   apply(Estimands,2,sd),
                   apply(Estimands,2,median),
                   apply(Estimands,2,function(x) quantile(x,probs[1])),
                   apply(Estimands,2,function(x) quantile(x,probs[2]))),4)
  colnames(est)<-c("Mean"," sd"," Median"," CI low"," CI up")
  print(est)
  
  parms<-round(cbind(apply(Theta,2,mean),  
                     apply(Theta,2,sd)),4)
  colnames(parms)<-c("Mean"," sd")
  print(parms)
  
  return(list(Theta=Theta, Estimands=Estimands))}


par.prior <- list(nu.c=0, nu.t=0, tau2.c=100^2, tau2.t=100^2, a.c=2, b2.c=0.01, a.t=2, b2.t=0.01)


chain.mB<-mcmc.m5(niter=25000, nburn=5000, thin=1,  par.prior, 
                  Outcome.obs=df_jobs$depress6, W=df_jobs$Z, seed=2022, theta.start=NULL)
          Mean     sd  Median  CI low   CI up
ate.fs -0.1362 0.0619 -0.1350 -0.2621 -0.0178
ate.sp -0.1363 0.0821 -0.1354 -0.3007  0.0219
           Mean     sd
beta.c   0.7022 0.0309
beta.t   0.6352 0.0218
sigma2.c 0.1231 0.0154
sigma2.t 0.1265 0.0110
## Overlapping histograms of the simulated posterior distribution of ATE.FS
hist(chain.mB$Estimands[,"ate.fs"], freq=FALSE, breaks = 20,
     main = "Average Treatment Effect", 
     xlab="",ylab="", 
     cex.lab=2.5, density=30, col="#FC4E07")

hist(chain.mB$Estimands[,"ate.sp"], freq=FALSE, breaks=20,add=TRUE, col="#00AFBB", density=20)
legend("topright", cex=c(0.8,0.8,0.8), lty=c(1,1,1), col= c("#FC4E07", "#00AFBB"), legend=c("ATE.FS","ATE.SP"))

6. (a) Bayesian model-based analysis with covariates.

Bayesian model-based analysis with covariates for the outcome variable “depression six months after the intervention assignment.” Assume that Yi(0) and Yi(1) are independent and are both log-normally distributed conditional on covariates.

Derive the posterior distributions of the finite sample average causal effect and the super-population average causal effect. Plot the resulting posterior distributions in a histogram and report the following summary statistics of the resulting posterior distributions: mean, standard deviation, median, 2.5% and 97.5% percentiles.

Compare the results with those obtained without condition on the covariates

mcmc.m6 <- function(niter, nburn, thin=1,  
                     par.prior, Outcome.obs, W, X,
                     seed=NULL, theta.start=NULL, cred.level = 0.95){
  
    Yobs <- log(Outcome.obs) # log transf
    
    Nc<- sum(1-W); Nt<- sum(W); N<- Nt+Nc
    XX <- as.matrix(cbind(1,X))
    nxx<-ncol(XX)
    
    draws<- seq((nburn+1), niter, by=thin)
    ndraws<- length(draws)
    j <- 0
    
    # Start values
    lm.w0<- summary(lm(Yobs[W==0] ~ X[W==0,]))
    lm.w1<- summary(lm(Yobs[W==1] ~ X[W==1,]))
    if(is.null(theta.start)==TRUE){
    
    theta <- list(beta.c =  as.numeric(lm.w0$coefficients[,1]) + rnorm(nxx,0, 0.1),
                  beta.t =  as.numeric(lm.w1$coefficients[,1]) + rnorm(nxx,0, 0.1),
                  sigma2.c =  as.numeric(lm.w0$sigma^2) + rnorm(1,0, 0.01), 
                  sigma2.t = as.numeric(lm.w1$sigma^2)  + rnorm(1,0, 0.01))
    
  }else{
    theta<- theta.start
  }
  
  Theta <- matrix(NA, ndraws,  length(unlist(theta)) )
  colnames(Theta) <- names(unlist(theta))
  
  Estimands<- matrix(NA, ndraws, 2)
  colnames(Estimands)<- c("ate.fs", "ate.sp")
  
  if(is.null(seed)==FALSE){
    set.seed(seed)}
  
  for(l in 1:niter){
    
    # Update beta.c
    Omega.c.obs   <- solve(solve(par.prior$Omega.c) + t(XX[W==0,])%*%XX[W==0,]/theta$sigma2.c)
    nu.c.obs      <- Omega.c.obs%*%(solve(par.prior$Omega.c)%*%par.prior$nu.c + t(XX[W==0,])%*%Yobs[W==0]/theta$sigma2.c)
    theta$beta.c  <- as.numeric(rmvnorm(1, mean= nu.c.obs, sigma=Omega.c.obs))
    
    # Update beta.t
    Omega.t.obs   <- solve(solve(par.prior$Omega.t) + t(XX[W==1,])%*%XX[W==1,]/theta$sigma2.t)
    nu.t.obs      <- Omega.t.obs%*%(solve(par.prior$Omega.t)%*%par.prior$nu.t + t(XX[W==1,])%*%Yobs[W==1]/theta$sigma2.t)
    theta$beta.t  <- as.numeric(rmvnorm(1, mean= nu.t.obs, sigma=Omega.t.obs))
    
    # Update sigma2.c
    a.c.obs        <- Nc + par.prior$a.c
    b2.c.obs       <-  {par.prior$a.c*par.prior$b2.c + sum({Yobs[W==0]-XX[W==0,]%*%theta$beta.c}^2)}/a.c.obs
    theta$sigma2.c <-  {a.c.obs*b2.c.obs}/rchisq(1, a.c.obs)
    
    # Update sigma2.t
    a.t.obs        <- Nt + par.prior$a.t
    b2.t.obs       <- {par.prior$a.t*par.prior$b2.t + sum({Yobs[W==1]-XX[W==1,]%*%theta$beta.t}^2)}/a.t.obs
    theta$sigma2.t <-  {a.t.obs*b2.t.obs}/rchisq(1, a.t.obs)      
    
    rm(Omega.c.obs, nu.c.obs, Omega.t.obs, nu.t.obs,  a.c.obs, b2.c.obs, a.t.obs, b2.t.obs)
    
    if(sum(l == draws)==1){
      j <- j+1
      
      Theta[j,]<- unlist(theta)
      
      ##Imputate the missing potential outcomes using Ymis | Yobs, W, X, theta
      Y0<-Y1<-NULL
      
      Y0[W==0]<- Outcome.obs[W==0] # no log transf
      Y0[W==1]<- exp(rnorm(Nt, XX[W==1,]%*%theta$beta.c, sqrt(theta$sigma2.c))) # inverse trans
      
      Y1[W==0]<- exp(rnorm(Nc, XX[W==0,]%*%theta$beta.t, sqrt(theta$sigma2.t))) # inverse trans
      Y1[W==1]<- Outcome.obs[W==1] # no log transf
      
      Estimands[j,"ate.fs"] <- mean(Y1)-mean(Y0)
      Estimands[j,"ate.sp"] <- mean(exp(XX%*%theta$beta.t + 0.5*theta$sigma2.t)) - mean(exp(XX%*%theta$beta.c + 0.5*theta$sigma2.c))
    }
  }
  
  # Summary statistics of the simulated posterior distribution of ATE.FS and ATE.SP
  probs<-c((1-cred.level)/2,1-(1-cred.level)/2)
  
  est<-round(cbind(apply(Estimands,2,mean),
                   apply(Estimands,2,sd),
                   apply(Estimands,2,median),
                   apply(Estimands,2,function(x) quantile(x,probs[1])),
                   apply(Estimands,2,function(x) quantile(x,probs[2]))),4)
  
  colnames(est)<-c("Mean"," sd"," Median"," CI low"," CI up")
  print(est)
  
  
  parms<-round(cbind(apply(Theta,2,mean),  
                     apply(Theta,2,sd)),4)
  colnames(parms)<-c("Mean"," sd")
  print(parms)
  
  return(list(Theta=Theta, Estimands=Estimands))
}


X <- as.matrix(df_jobs[, c("sex","age","race","nonmarried","educ","EconHard","assertive","motivation","depress0")])
ncov<- ncol(X)

par.prior <- list(nu.c=rep(0, {ncov+1}), Omega.c=diag(100^2,{ncov+1}), 
                  nu.t=rep(0, {ncov+1}), Omega.t=diag(100^2,{ncov+1}),
                  a.c=2, b2.c=0.01, a.t=2, b2.t=0.01)

chain.mC<-mcmc.m6(niter=20000, nburn=5000, thin=1,  
                  par.prior, 
                  Outcome.obs=Yobs, W=Z, X=X, 
                  seed=2022, theta.start=NULL)
          Mean     sd  Median  CI low   CI up
ate.fs -0.1864 0.0670 -0.1843 -0.3227 -0.0614
ate.sp -0.1876 0.0863 -0.1846 -0.3640 -0.0261
            Mean     sd
beta.c1   0.0984 0.4469
beta.c2  -0.0182 0.0670
beta.c3   0.0023 0.0033
beta.c4   0.1368 0.0950
beta.c5  -0.0540 0.0655
beta.c6   0.0034 0.0164
beta.c7   0.0674 0.0389
beta.c8  -0.0288 0.0412
beta.c9   0.0543 0.0398
beta.c10  0.0261 0.1114
beta.t1   0.6888 0.2978
beta.t2   0.0320 0.0439
beta.t3   0.0006 0.0023
beta.t4   0.0546 0.0552
beta.t5  -0.0278 0.0465
beta.t6  -0.0123 0.0108
beta.t7   0.0758 0.0280
beta.t8  -0.0293 0.0247
beta.t9  -0.0525 0.0266
beta.t10  0.0692 0.0724
sigma2.c  0.1220 0.0159
sigma2.t  0.1237 0.0109
## Overlapping histograms of the simulated posterior distribution of ATE.FS
hist(chain.mC$Estimands[,"ate.fs"], freq=FALSE, breaks = 20,
     main = "Average Treatment Effect", 
     xlab="",ylab="", 
     cex.lab=2.5, density=30, col="#FC4E07")

hist(chain.mC$Estimands[,"ate.sp"], freq=FALSE, breaks=20,add=TRUE, col="#00AFBB", density=20)

legend("topright", cex=c(0.8,0.8,0.8), lty=c(1,1,1), col= c("#FC4E07", "#00AFBB"), legend=c("ATE.FS","ATE.SP"))

## Overlapping histograms of the simulated posterior distribution of ATE.FS
hist(chain.mB$Estimands[,"ate.fs"], freq=FALSE, breaks = 20,
     main = "Finite-Sample Average Treatment Effect", 
     xlab="",ylab="", 
     cex.lab=2.5, density=30, col="#FC4E07")  # "#FC4E07", "#00AFBB"

hist(chain.mC$Estimands[,"ate.fs"], freq=FALSE, breaks=20,add=TRUE, col="#00AFBB", density=20)
legend("topright", cex=c(0.8,0.8,0.8), lty=c(1,1,1), col= c("#FC4E07", "#00AFBB"), legend=c("Model B","Model C")) 


## Overlapping histograms of the simulated posterior distribution of ATE.PS
hist(chain.mB$Estimands[,"ate.sp"], freq=FALSE, breaks = 20,
     main = "Super-Population Average Treatment Effect", 
     xlab="",ylab="", 
     cex.lab=2.5, density=30, col="#FC4E07")  # "#FC4E07", "#00AFBB"

hist(chain.mC$Estimands[,"ate.sp"], freq=FALSE, breaks=20,add=TRUE, col="#00AFBB", density=20)
legend("topright", cex=c(0.8,0.8,0.8), lty=c(1,1,1), col= c("#FC4E07", "#00AFBB"), legend=c("Model B","Model C"))

Looking at the that distributions, going from the model B without condition on covariates to model C with it, this seem to have an effect on the outcome, considering, e.g. that the curve are shifted to the left.

PART 2 | Observational Studies

1. Load the dataset. Temporarily remove the outcome, OUTCOME, from the data set.

library(ggplot2)
library(MatchIt)
# import file
df_lux_all <- read.table('TrainingLux.txt')
df_lux_all
# df withou Outcome
df_lux <- df_lux_all[, !(names(df_lux_all) == "Outcome")]
df_lux

# Covariates
X <- df_lux[, !(names(df_lux) == "TREAT")]
X

2. For each covariate, display the mean within each treatment group and the standardized difference in a table.

std_diff <- function(x, treat){
  
  ### Param checks
  # Check for NAs in x
  if(any(is.na(x))){warning("NAs removed, check x and think of other options")}
  
  # Check for NAs in treat
  if(any(is.na(treat))){stop("treatment indicator 'treat' is not supposed to contain NA, check it!")}
  
  # Mean
  mean <- mean(x, na.rm = TRUE)
  
  ### Mean of 'x' among treated and control units
  mean.t <- mean(x[treat == 1], na.rm = TRUE)
  mean.c <- mean(x[treat == 0], na.rm = TRUE)
  
  ### Variance of 'x' among treated and control units
  var.t <- var(x[treat == 1], na.rm = TRUE)
  var.c <- var(x[treat == 0], na.rm = TRUE)
  
  ### Standardized difference in means
  std.diff <- (mean.t - mean.c) / sqrt((var.t + var.c)/2)
  
  ### Returning results
  res <- c(Mean = mean, Mean.t = mean.t, Mean.c = mean.c, Sd.t = sqrt(var.t), Sd.c = sqrt(var.c), Std.Mean.Diff = std.diff)
  return(res)
}

t(sapply(X, std_diff, treat = df_lux$TREAT, simplify = TRUE))
                    Mean      Mean.t      Mean.c      Sd.t       Sd.c Std.Mean.Diff
age          34.51204676 36.82580645 34.43507987 9.0586485 11.5128683    0.23079412
gender        0.47691532  0.64884793  0.47119600 0.4775505  0.4991773    0.36368043
married       0.42436651  0.55576037  0.41999571 0.4971102  0.4935654    0.27408325
natio1        0.29968548  0.05529954  0.30781494 0.2286694  0.4615966   -0.69324097
natio2        0.29989318  0.27004608  0.30088604 0.4441881  0.4586503   -0.06830902
natio3        0.19672423  0.27373272  0.19416255 0.4460790  0.3955607    0.18874397
natio4        0.08417898  0.18709677  0.08075543 0.3901690  0.2724633    0.31601927
natio5        0.11951813  0.21382488  0.11638103 0.4101937  0.3206862    0.26467071
educ1         0.27256543  0.27741935  0.27240396 0.4479317  0.4452035    0.01123093
educ2         0.48991158  0.34009217  0.49489530 0.4739584  0.4999816   -0.31777709
educ3         0.23752300  0.38248848  0.23270074 0.4862190  0.4225596    0.32884047
EmployLevel1  0.17266038  0.09677419  0.17518472 0.2957864  0.3801309   -0.23022667
EmployLevel2  0.39950151  0.43778802  0.39822792 0.4963434  0.4895404    0.08025116
EmployLevel3  0.33487627  0.37972350  0.33338443 0.4855418  0.4714298    0.09683471
EmployLevel4  0.07735446  0.07465438  0.07744428 0.2629540  0.2672992   -0.01052254
EmployLevel5  0.01560738  0.01105991  0.01575865 0.1046311  0.1245423   -0.04085212
skill         0.95178328  0.94746544  0.95192691 0.2232053  0.2139240   -0.02040800
sector0       0.32959468  0.15023041  0.33556121 0.3574619  0.4721935   -0.44255413
sector1       0.29915139  0.36405530  0.29699237 0.4813861  0.4569402    0.14289312
sector2       0.10438550  0.17419355  0.10206334 0.3794507  0.3027362    0.21014302
sector3       0.14025874  0.16774194  0.13934451 0.3738093  0.3463110    0.07881112
sector4       0.12660970  0.14377880  0.12603857 0.3510271  0.3318979    0.05193331
work_12       0.64289953  0.75391705  0.63920655 0.4309261  0.4802381    0.25142095
nb_mthbf_12   6.39051095  4.97050691  6.43774719 3.6329590  5.4659510   -0.31615753

3. Estimate a propensity score for each unit in the observational study using the fitted values from a logistic regression with main effects for all of the covariates contained in the data set (leave out transformations and interactions here, but we would generally want to explore these other terms).

Compare graphically the distributions of estimated propensity scores within the treatment groups and explain what you see.

# propensity score
mod <- glm(TREAT~., data=df_lux, family=binomial(link=logit))
summary(mod)

Call:
glm(formula = TREAT ~ ., family = binomial(link = logit), data = df_lux)

Deviance Residuals: 
    Min       1Q   Median       3Q      Max  
-1.5393  -0.2453  -0.1609  -0.0911   3.7833  

Coefficients: (4 not defined because of singularities)
              Estimate Std. Error z value Pr(>|z|)    
(Intercept)  -1.703582   0.409327  -4.162 3.16e-05 ***
age           0.020601   0.003525   5.844 5.09e-09 ***
gender        0.731334   0.070656  10.351  < 2e-16 ***
married       0.229798   0.073204   3.139 0.001695 ** 
natio1       -2.170796   0.155755 -13.937  < 2e-16 ***
natio2       -0.659103   0.100443  -6.562 5.31e-11 ***
natio3       -0.434414   0.102592  -4.234 2.29e-05 ***
natio4       -0.021194   0.111200  -0.191 0.848847    
natio5              NA         NA      NA       NA    
educ1        -0.461297   0.086224  -5.350 8.80e-08 ***
educ2        -0.983716   0.093343 -10.539  < 2e-16 ***
educ3               NA         NA      NA       NA    
EmployLevel1 -0.586041   0.326075  -1.797 0.072294 .  
EmployLevel2 -0.145899   0.311767  -0.468 0.639803    
EmployLevel3 -0.058455   0.310292  -0.188 0.850573    
EmployLevel4  0.070019   0.324800   0.216 0.829320    
EmployLevel5        NA         NA      NA       NA    
skill        -0.295697   0.149766  -1.974 0.048337 *  
sector0      -2.309417   0.157679 -14.646  < 2e-16 ***
sector1       0.368923   0.105600   3.494 0.000477 ***
sector2       0.257943   0.120069   2.148 0.031691 *  
sector3      -0.436960   0.120721  -3.620 0.000295 ***
sector4             NA         NA      NA       NA    
work_12       0.959554   0.136134   7.049 1.81e-12 ***
nb_mthbf_12  -0.263125   0.009887 -26.614  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 9590.8  on 33701  degrees of freedom
Residual deviance: 7702.0  on 33681  degrees of freedom
AIC: 7744

Number of Fisher Scoring iterations: 8
pscores <- mod$fitted.values

std_diff(pscores, treat = df_lux$TREAT)
         Mean        Mean.t        Mean.c          Sd.t          Sd.c Std.Mean.Diff 
   0.03219394    0.11820900    0.02933266    0.11267286    0.05052386    1.01788126 
# dens overlap
density_overlap <- function(x, treat, alpha = 0.25){
  
  ### Formatting data
  data <- data.frame(Legend = c(rep("Treated", sum(treat)), rep("Controls", sum(treat == 0))), Value = c(x[treat ==1], x[treat == 0]))
  
  ### Calling 'ggplot'
  ggplot(data, aes(x = Value, fill = Legend)) + geom_density(alpha = alpha)
}


# Hist overlap
hist_overlap <- function(x, treat, alpha = 0.5, ...){
  
  ### Formatting data
  data <- data.frame(Legend = c(rep("Treated", sum(treat)), rep("Controls", sum(treat == 0))), Value = c(x[treat ==1], x[treat == 0]))
  
  ### Calling 'ggplot'
  ggplot(data, aes(x = Value, fill = Legend, after_stat(density))) + geom_histogram(alpha = alpha, position = "identity", ...)
}

density_overlap(x = pscores, treat = df_lux$TREAT)

hist_overlap(x = pscores, treat = df_lux$TREAT, bins = 20)

The non-treated distribution is more condensed to the origin, showing a significant difference on distributions of the treated and control groups.

We already know that in this observational study, so in this dataset, subjects were not randomly assigned to treatment and this graph underline this statment. The treatment assignment have been affected by specific values of units’ covariates, in a preferential way considering some characteristics, showing a lack of randomness.

4. What are the implications of the fact that the propensity score is a balancing score?

Propensity score is useful for achieving covariate balance in observational studies, since the lack of balance could influence the reliability of causal effect estimates, as the treated and control groups may not be comparable with respect to observed covariates.

The propensity score serves as a summary measure of the covariates, and how the distribution of X appears balanced between treated and control groups.

The propensity score is the coarsest balancing score, it is a function of every balancing score, so it provides the biggest benefit in terms of reducing the number of variables we need to adjust for.

Its advantage lies in significantly reducing the number of variables requiring adjustment.

5. Trimming and subclassification on propensity score. Now assess balance and create balanced groups.

(a) In order to do so, first discard control units with estimated propensity scores lower than the minimum of the active treated units estimated propensity scores or higher than the maximum of the active treatment units estimated propensity scores.

How many units did you discard? Why is it important to discard these units?

### 2) TRIMMING

# We want to discard control units having PS lower than the min PS for the treated. 
# We could also discard treated units for a common support but in this case we prefer to salvage all the n treated

by(pscores,df_lux$TREAT,summary) 
df_lux$TREAT: 0
    Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
0.000378 0.004740 0.013512 0.029333 0.030593 0.694177 
--------------------------------------------------------------------------------------------------- 
df_lux$TREAT: 1
     Min.   1st Qu.    Median      Mean   3rd Qu.      Max. 
0.0007796 0.0370989 0.0756885 0.1182090 0.1721260 0.6025573 
mincut <- min(pscores[df_lux$TREAT==1])
maxcut <- max(pscores[df_lux$TREAT==1])

df_lux2 <- df_lux[pscores>=mincut & pscores<=maxcut,]

nrow(df_lux) - nrow(df_lux2)  # number discarded
[1] 370
table(df_lux2$TREAT)          # only controls discarded, as we wanted

    0     1 
32247  1085 
discarded <- which(pscores < mincut | pscores > maxcut) # discarded units
#discarded

I’ve discarded 370 units. It’s important to discard these units to achieve common support between the treatment and control groups, ensuring that the distribution of estimated propensity scores overlaps for both groups. This helps in creating balanced groups and improve the lack of randomization. Removing units make the estimated treatment effects more reliable and reduce the potential for bias.

# 2.1) Standardized Diff. in Mean for the trimmed data
X.trim <- df_lux2[, !(names(df_lux2) == "TREAT")]
t(sapply(X.trim, std_diff, treat = df_lux2$TREAT, simplify = TRUE))
                    Mean      Mean.t      Mean.c      Sd.t       Sd.c Std.Mean.Diff
age          34.66965679 36.82580645 34.59710981 9.0586485 11.4667373   0.215685561
gender        0.48193928  0.64884793  0.47632338 0.4775505  0.4994468   0.353084345
married       0.42883715  0.55576037  0.42456663 0.4971102  0.4942847   0.264663876
natio1        0.29206168  0.05529954  0.30002791 0.2286694  0.4582769  -0.675762473
natio2        0.30322213  0.27004608  0.30433839 0.4441881  0.4601338  -0.075829142
natio3        0.19890796  0.27373272  0.19639036 0.4460790  0.3972733   0.183110105
natio4        0.08502340  0.18709677  0.08158899 0.3901690  0.2737418   0.313059906
natio5        0.12078483  0.21382488  0.11765436 0.4101937  0.3222034   0.260743540
educ1         0.27517101  0.27741935  0.27509536 0.4479317  0.4465692   0.005196181
educ2         0.48481939  0.34009217  0.48968896 0.4739584  0.4999014  -0.307115557
educ3         0.24000960  0.38248848  0.23521568 0.4862190  0.4241401   0.322799140
EmployLevel1  0.17037682  0.09677419  0.17285329 0.2957864  0.3781263  -0.224116604
EmployLevel2  0.39973599  0.43778802  0.39845567 0.4963434  0.4895878   0.079785340
EmployLevel3  0.33589344  0.37972350  0.33441871 0.4855418  0.4717942   0.094637885
EmployLevel4  0.07821313  0.07465438  0.07833287 0.2629540  0.2686988  -0.013837132
EmployLevel5  0.01578063  0.01105991  0.01593947 0.1046311  0.1252433  -0.042284478
skill         0.95139806  0.94746544  0.95153037 0.2232053  0.2147598  -0.018559388
sector0       0.32434297  0.15023041  0.33020126 0.3574619  0.4702927  -0.430856753
sector1       0.30220209  0.36405530  0.30012094 0.4813861  0.4583174   0.136032475
sector2       0.10554422  0.17419355  0.10323441 0.3794507  0.3042695   0.206324173
sector3       0.14034561  0.16774194  0.13942382 0.3738093  0.3463936   0.078582351
sector4       0.12756510  0.14377880  0.12701957 0.3510271  0.3330001   0.048984655
work_12       0.64784591  0.75391705  0.64427699 0.4309261  0.4787392   0.240723538
nb_mthbf_12   6.43693748  4.97050691  6.48627779 3.6329590  5.4559512  -0.327029654
# 2.2) Re-estimation of the propensity score
mod2 <- glm(TREAT~., data = df_lux2, family = binomial(link=logit))
summary(mod2)

Call:
glm(formula = TREAT ~ ., family = binomial(link = logit), data = df_lux2)

Deviance Residuals: 
    Min       1Q   Median       3Q      Max  
-1.3347  -0.2463  -0.1614  -0.0931   3.7869  

Coefficients: (4 not defined because of singularities)
              Estimate Std. Error z value Pr(>|z|)    
(Intercept)  -1.669433   0.409248  -4.079 4.52e-05 ***
age           0.020366   0.003528   5.773 7.80e-09 ***
gender        0.737173   0.070708  10.426  < 2e-16 ***
married       0.237653   0.073236   3.245 0.001174 ** 
natio1       -2.170740   0.155773 -13.935  < 2e-16 ***
natio2       -0.663417   0.100434  -6.605 3.96e-11 ***
natio3       -0.439488   0.102611  -4.283 1.84e-05 ***
natio4       -0.015758   0.111145  -0.142 0.887255    
natio5              NA         NA      NA       NA    
educ1        -0.470894   0.086238  -5.460 4.75e-08 ***
educ2        -0.994196   0.093392 -10.645  < 2e-16 ***
educ3               NA         NA      NA       NA    
EmployLevel1 -0.593443   0.326219  -1.819 0.068887 .  
EmployLevel2 -0.150265   0.311892  -0.482 0.629958    
EmployLevel3 -0.058931   0.310390  -0.190 0.849418    
EmployLevel4  0.066706   0.324933   0.205 0.837345    
EmployLevel5        NA         NA      NA       NA    
skill        -0.325093   0.149084  -2.181 0.029213 *  
sector0      -2.307809   0.157831 -14.622  < 2e-16 ***
sector1       0.382290   0.105687   3.617 0.000298 ***
sector2       0.259421   0.120182   2.159 0.030883 *  
sector3      -0.439174   0.120826  -3.635 0.000278 ***
sector4             NA         NA      NA       NA    
work_12       0.979482   0.136277   7.187 6.60e-13 ***
nb_mthbf_12  -0.265139   0.009910 -26.755  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 9566.4  on 33331  degrees of freedom
Residual deviance: 7690.7  on 33311  degrees of freedom
AIC: 7732.7

Number of Fisher Scoring iterations: 8
pscores2 <- mod2$fitted.values

# 2.3) Balance assessment (Standardized Diff. in Mean and graphical checks)
std_diff(pscores2, treat = df_lux2$TREAT)
         Mean        Mean.t        Mean.c          Sd.t          Sd.c Std.Mean.Diff 
   0.03255130    0.11918544    0.02963636    0.11419756    0.05062333    1.01381995 
hist_overlap(pscores2, treat = df_lux2$TREAT)

(b) Using the units remaining after 5(a), create five subclasses based on the estimated propensity score.

You are allowed to choose size and bounds of the subclasses. You are supposed to create the best subclasses based on your own reasoning. Create a table showing the number of treated and control units within each of the five subclasses.

Briefly comment and explain your choice. (Hint: look at the distribution of the propensity score in the two groups, check the overlap, check the balance within subclasses and the number of treated and control units, etc).

### 3) SUBCLASSIFICATION BASED ON THE PROPENSITY SCORE

# The goal is to create subclasses of treated and control units sharing
# similar values for the propensity score

# 3.1) Defining subclasses from the inspection of the quantiles table
quant.tab <- data.frame(Quantiles = seq(0,1, by=0.05),
                        PS.Controls = as.numeric(quantile(pscores2[df_lux2$TREAT==0],probs=seq(0,1, by=0.05))),
                        PS.Treated = as.numeric(quantile(pscores2[df_lux2$TREAT==1],probs=seq(0,1, by=0.05))),
                        PS.Whole = as.numeric(quantile(pscores2,probs=seq(0,1, by=0.05))))

breaks <- quantile(pscores2, c(.65, .80, .85, .9))  #, .95))  # .4, .75, .85, .9, .95)) 
bins <- rep(NA,nrow(df_lux2))
bins[pscores2<=breaks[1]] <- 1
bins[pscores2>breaks[1] & pscores2<=breaks[2]] <- 2
bins[pscores2>breaks[2] & pscores2<=breaks[3]] <- 3
bins[pscores2>breaks[3] & pscores2<=breaks[4]] <- 4
bins[pscores2>breaks[4]] <-5 # & pscores2<=breaks[5]] <- 5
#bins[pscores2>breaks[5]] <- 6

table(bins, df_lux2$TREAT)        # Number of T and C in each subclass
    
bins     0     1
   1 21518   148
   2  4842   160
   3  1579    88
   4  1523   140
   5  2785   549
table(bins)                    # Tot number of people in each subclass
bins
    1     2     3     4     5 
21666  5002  1667  1663  3334 
# 3.2) Balance assessment within each block
# Standardized Diff. in Mean
mapply(1:length(unique(bins)), FUN = function(b)(std_diff(pscores2[bins == b], treat = df_lux2$TREAT[bins == b])))
                     [,1]        [,2]        [,3]        [,4]       [,5]
Mean          0.008919212 0.030725485 0.046469389 0.062117368 0.16715712
Mean.t        0.014628061 0.031783538 0.046538920 0.063134609 0.19878253
Mean.c        0.008879947 0.030690522 0.046465514 0.062023859 0.16092288
Sd.t          0.005907308 0.005312641 0.003356584 0.006169522 0.11219332
Sd.c          0.006383441 0.005063838 0.003501418 0.006067369 0.08937412
Std.Mean.Diff 0.934655235 0.210611243 0.021402553 0.181534864 0.37326812
# mapply(X.trim, FUN = function(x)(std_diff_block(x, treat = df_lux2$TREAT, blocks = bins, weights = "total")))
  1. Use descriptive tools (graphs and statistics) to assess covariate balance.
# Graphical balance assessment
density_overlap <- function(x, treat, alpha = 0.25){
  
  ### Formatting data
  data <- data.frame(Legend = c(rep("Treated", sum(treat)), rep("Controls", sum(treat == 0))), Value = c(x[treat ==1], x[treat == 0]))
  
  ### Calling 'ggplot'
  ggplot(data, aes(x = Value, fill = Legend)) + geom_density(alpha = alpha)
}

mapply(unique(bins)[order(unique(bins))], 
       FUN = function(B)(density_overlap(x = pscores2[bins == B], treat = df_lux2$TREAT[bins == B]) +
                           ggtitle(paste("Overlap, block", B))), SIMPLIFY = FALSE)
[[1]]

[[2]]

[[3]]

[[4]]

[[5]]

After some attemps I reached this as my best fitting overlap between Trated and Control. The first block has a significant area of non-overlapping, but the other blocks show a better situation. That’s underlined also in the stat table where: Mean.t = 0.014628061, Mean.c = 0.008879947 in case of block 1, showing a significant difference. Similarly, in block 5, but still with a good fitting.

6. Analysis phase

  1. Now that the study design phase is complete, read in the outcome, Outcome.

  2. Naively pretending that this observational study was actually a completely randomized experiment, and ignoring covariates, calculate a Neyman estimate of the average treatment effect and a large sample 95% interval and enter both in a table.

  3. Calculate the naive Neyman estimate as in the previous point, but on the subset of units obtained in 5(a).

Report it in the table and comment briefly.

# analysis
df_lux$Outcome <- df_lux_all$Outcome
df_lux

df_lux2$Outcome <- df_lux_all$Outcome[-discarded]
df_lux2

neyman <- function(outcome, treat, alpha){
  
  # Settings
  Yt <- outcome[treat == 1]
  Yc <- outcome[treat == 0]
  Nt <- length(Yt)
  Nc <- length(Yc)
  
  # ATE
  ate <- mean(Yt) - mean(Yc)
  
  # Variance estimator
  Var.t <- var(Yt)
  Var.c <- var(Yc)
  Var <- Var.t / Nt + Var.c / Nc
  
  # Exporting results
  res <- cbind(ATE = ate, Var = Var, int.lower = ate - sqrt(Var)*qnorm(1-alpha/2), int.upper = ate + sqrt(Var)*qnorm(1-alpha/2))
  return(res)
}
### 2) ATE, Naive Neyman (i.e., Neyman as if dataset was randomized)
ney.naive <- neyman(outcome = df_lux$Outcome, treat = df_lux$TREAT, alpha = 0.05)
ney.naive
             ATE          Var   int.lower   int.upper
[1,] -0.02727316 0.0002374124 -0.05747266 0.002926344
### 3) ATE, Neyman on the trimmed subset
ney.trimm <- neyman(outcome = df_lux2$Outcome, treat = df_lux2$TREAT, alpha = 0.05)
ney.trimm
             ATE          Var   int.lower   int.upper
[1,] -0.02608443 0.0002375001 -0.05628951 0.004120645
ney <- rbind(ney.naive,ney.trimm)

rownames(ney) <- c("ATE Naive Neyman", "ATE Naive Neyman on trimmed subset")
round(ney,2)
                                     ATE Var int.lower int.upper
ATE Naive Neyman                   -0.03   0     -0.06         0
ATE Naive Neyman on trimmed subset -0.03   0     -0.06         0

The interval on the trimmed subset is slightly narrower.

7. Your own preferred analysis. Use any other methods (Matching with or without replacement, Bias-adjusted estimators, IPTW, regression, DR, CART, BART etc…) for estimating ATE, ATT or other causal effects you believe are relevant for the study. You may also investigate treatment effect heterogeneity and estimate CATEs. In R you can use any package, including the ones suggested during lectures, e.g., Matchit).

Please briefly present the method and explain your preference and discuss results you have obtained.

### 5) Estimating ATT on matched data

# 4.2) Non-Exact Matching: Nearest Neighbors with Propensity Score
 
# Without replacement, discarding control units  
# outside the support of the distance measure of the treated units 
m.nn1 <- matchit(TREAT~ ., data = df_lux, method = "nearest", discard='control', distance = "glm")
m.nn1
A matchit object
 - method: 1:1 nearest neighbor matching without replacement
 - distance: Propensity score [common support]
             - estimated with logistic regression
 - common support: control units dropped
 - number of obs.: 33702 (original), 2170 (matched)
 - target estimand: ATT
 - covariates: age, gender, married, natio1, natio2, natio3, natio4, natio5, educ1, educ2, educ3, EmployLevel1, EmployLevel2, EmployLevel3, EmployLevel4, EmployLevel5, skill, sector0, sector1, sector2, sector3, sector4, work_12, nb_mthbf_12, Outcome
summary(m.nn1) ## how many units are matched? --> "THE" nearest neighbor

Call:
matchit(formula = TREAT ~ ., data = df_lux, method = "nearest", 
    distance = "glm", discard = "control")

Summary of Balance for All Data:
             Means Treated Means Control Std. Mean Diff. Var. Ratio eCDF Mean eCDF Max
distance            0.1185        0.0293          0.7892     4.9882    0.3765   0.5563
age                36.8258       34.4351          0.2639     0.6191    0.0582   0.1921
gender              0.6488        0.4712          0.3722          .    0.1777   0.1777
married             0.5558        0.4200          0.2732          .    0.1358   0.1358
natio1              0.0553        0.3078         -1.1048          .    0.2525   0.2525
natio2              0.2700        0.3009         -0.0695          .    0.0308   0.0308
natio3              0.2737        0.1942          0.1785          .    0.0796   0.0796
natio4              0.1871        0.0808          0.2727          .    0.1063   0.1063
natio5              0.2138        0.1164          0.2377          .    0.0974   0.0974
educ1               0.2774        0.2724          0.0112          .    0.0050   0.0050
educ2               0.3401        0.4949         -0.3268          .    0.1548   0.1548
educ3               0.3825        0.2327          0.3082          .    0.1498   0.1498
EmployLevel1        0.0968        0.1752         -0.2652          .    0.0784   0.0784
EmployLevel2        0.4378        0.3982          0.0797          .    0.0396   0.0396
EmployLevel3        0.3797        0.3334          0.0955          .    0.0463   0.0463
EmployLevel4        0.0747        0.0774         -0.0106          .    0.0028   0.0028
EmployLevel5        0.0111        0.0158         -0.0449          .    0.0047   0.0047
skill               0.9475        0.9519         -0.0200          .    0.0045   0.0045
sector0             0.1502        0.3356         -0.5187          .    0.1853   0.1853
sector1             0.3641        0.2970          0.1394          .    0.0671   0.0671
sector2             0.1742        0.1021          0.1902          .    0.0721   0.0721
sector3             0.1677        0.1393          0.0760          .    0.0284   0.0284
sector4             0.1438        0.1260          0.0506          .    0.0177   0.0177
work_12             0.7539        0.6392          0.2663          .    0.1147   0.1147
nb_mthbf_12         4.9705        6.4377         -0.4039     0.4418    0.1709   0.3960
Outcome             0.4691        0.4964         -0.0547          .    0.0273   0.0273

Summary of Balance for Matched Data:
             Means Treated Means Control Std. Mean Diff. Var. Ratio eCDF Mean eCDF Max Std. Pair Dist.
distance            0.1185        0.1184          0.0011     1.0076    0.0000   0.0028          0.0015
age                36.8258       36.8313         -0.0006     0.6900    0.0323   0.0802          1.1654
gender              0.6488        0.6581         -0.0193          .    0.0092   0.0092          0.8264
married             0.5558        0.5438          0.0241          .    0.0120   0.0120          0.8774
natio1              0.0553        0.0470          0.0363          .    0.0083   0.0083          0.3105
natio2              0.2700        0.2654          0.0104          .    0.0046   0.0046          0.7619
natio3              0.2737        0.2719          0.0041          .    0.0018   0.0018          0.7814
natio4              0.1871        0.2129         -0.0662          .    0.0258   0.0258          0.6759
natio5              0.2138        0.2028          0.0270          .    0.0111   0.0111          0.6834
educ1               0.2774        0.2765          0.0021          .    0.0009   0.0009          0.8419
educ2               0.3401        0.3392          0.0019          .    0.0009   0.0009          0.8424
educ3               0.3825        0.3843         -0.0038          .    0.0018   0.0018          0.8268
EmployLevel1        0.0968        0.1097         -0.0436          .    0.0129   0.0129          0.5611
EmployLevel2        0.4378        0.4157          0.0446          .    0.0221   0.0221          0.8769
EmployLevel3        0.3797        0.3733          0.0133          .    0.0065   0.0065          0.8413
EmployLevel4        0.0747        0.0922         -0.0666          .    0.0175   0.0175          0.5085
EmployLevel5        0.0111        0.0092          0.0176          .    0.0018   0.0018          0.1939
skill               0.9475        0.9410          0.0289          .    0.0065   0.0065          0.4668
sector0             0.1502        0.1779         -0.0774          .    0.0276   0.0276          0.3611
sector1             0.3641        0.3373          0.0555          .    0.0267   0.0267          0.8026
sector2             0.1742        0.1705          0.0097          .    0.0037   0.0037          0.7047
sector3             0.1677        0.1613          0.0173          .    0.0065   0.0065          0.6833
sector4             0.1438        0.1530         -0.0263          .    0.0092   0.0092          0.7092
work_12             0.7539        0.7263          0.0642          .    0.0276   0.0276          0.6248
nb_mthbf_12         4.9705        4.6876          0.0779     0.6686    0.0929   0.1585          0.9851
Outcome             0.4691        0.4387          0.0609          .    0.0304   0.0304          0.9253

Sample Sizes:
          Control Treated
All         32617    1085
Matched      1085    1085
Unmatched   31280       0
Discarded     252       0
plot(summary(m.nn1))

summary(m.nn1$distance)
     Min.   1st Qu.    Median      Mean   3rd Qu.      Max. 
0.0003871 0.0049891 0.0141478 0.0321939 0.0327900 0.7005737 
summary(pscores2)
     Min.   1st Qu.    Median      Mean   3rd Qu.      Max. 
0.0007632 0.0052455 0.0143247 0.0325513 0.0331730 0.6161660 
# Obtain matched dataset from MatchIt output
m.mydata <- match.data(m.nn1)
head(m.mydata)

# Neyman's method for estimating causal effects is used to calculate the Average Treatment Effect on the Treated (ATT)
ney_match <- neyman(outcome = m.mydata$Outcome, treat = m.mydata$TREAT, alpha = 0.05)
ney_match
            ATE          Var   int.lower  int.upper
[1,] 0.03041475 0.0004569098 -0.01148036 0.07230985
# Regression
summary(lm(Outcome~TREAT, data=m.mydata))

Call:
lm(formula = Outcome ~ TREAT, data = m.mydata)

Residuals:
    Min      1Q  Median      3Q     Max 
-0.4691 -0.4691 -0.4387  0.5309  0.5613 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  0.43871    0.01511  29.025   <2e-16 ***
TREAT        0.03041    0.02138   1.423    0.155    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.4979 on 2168 degrees of freedom
Multiple R-squared:  0.000933,  Adjusted R-squared:  0.0004722 
F-statistic: 2.025 on 1 and 2168 DF,  p-value: 0.1549

I’ve used MatchIt function with the “nearest” method to perform nearest neighbor matching without replacement and Neyman’s method to estimate the ATT using the matched dataset. The balance assessment shows that the matching procedure has improved balance across covariates in the matched dataset.

The estimated ATT is approximately 0.0304 with a confidence interval spanning from -0.0115 to 0.0723, thus the treatment effect may be positive, negative or zero.

The linear regression results confirm the estimated treatment effect, but with the absence of statistical significance in the regression results suggests that there isn’t strong evidence to reject the null hypothesis that the treatment effect is zero.

LS0tCnRpdGxlOiAiQmF5ZXNpYW4gSW5mZXJlbmNlICYgQ2F1c2FsIE1MIHwgR2lhbm1hcmNvIFMuIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgoKIyBFeHBlcmltZW50YWwgU3R1ZGllcyB8IEpPQlMgSUkKCmBgYHtyfQpsaWJyYXJ5KGhhdmVuKQpsaWJyYXJ5KE1DTUNwYWNrKQpsaWJyYXJ5KG12dG5vcm0pCgojIGNsZWFuIHdvcmtzcGFjZQpybShsaXN0PWxzKCkpIAoKIyBzZXQgd29ya2luZyBkaXJlY3RvcnkgaW4gUlN0dWRpbyB0byBkaXJlY3Rvcnkgd2hlcmUgY3VycmVudGx5IGFjdGl2ZSBzY3JpcHQKc2V0d2QoZGlybmFtZShyc3R1ZGlvYXBpOjpnZXRBY3RpdmVEb2N1bWVudENvbnRleHQoKSRwYXRoKSkKYGBgCgoKIyMgMS4gTG9hZCBkYXRhc2V0CgpgYGB7cn0KIyBpbXBvcnQgZmlsZQpkZl9qb2JzIDwtIHJlYWRfZHRhKCdKT0JTSUlfSFIuZHRhJykKZGZfam9icwpgYGAKT25seSA1NCUgb2YgaW5kaXZpZHVhbHMgd2hvIHdlcmUgYXNzaWduZWQgdG8gdGhlIGludGVydmVudGlvbiBhY3R1YWxseSByZWNlaXZlZCB0aGUgdHJlYXRtZW50LgpJbiB0aGUgbGl0ZXJhdHVyZSwgdGhpcyBwcm9ibGVtIGlzIGtub3duIGFzIG5vbmNvbXBsaWFuY2UuCkhlcmUgd2UgaWdub3JlIG5vbmNvbXBsaWFuY2UgaXNzdWVzIGZvY3VzaW5nIG9uIGFzc2Vzc2luZyBjYXVzYWwgZWZmZWN0cyBvZiB0aGUgYXNzaWdubWVudCB0byB0aGUgdHJlYXRtZW50IChJbnRlbnRpb24tdG8tdHJlYXQgYW5hbHlzaXMpLgoKU28sIEkgZHJvcCB0aGUgVyB2YXJpYWJsZSBhbmQgZm9jdXMgb24gdGhlIGFzc2lnbm1lbnQgdmFyaWFibGUgWiBmb3IgYXNzZXNzaW5nIHRoZSBjYXVzYWwgZWZmZWN0cyBvZiB0aGUgdHJlYXRtZW50IHRvIGVuc3VyZXMgdGhhdCBhbmFseXNpcyBhbGlnbnMgd2l0aCB0aGUgaW50ZW50aW9uLXRvLXRyZWF0IHByaW5jaXBsZS4KCmBgYHtyfQojIERyb3AgdGhlIFcgdmFyaWFibGUKZGZfam9icyA8LSBkZl9qb2JzWywgIShuYW1lcyhkZl9qb2JzKSA9PSAiVyIpXQojIGRmX2pvYnMKCiMjIyBFeHRyYQp0YWJsZShkZl9qb2JzJFopCnByb3AudGFibGUodGFibGUoZGZfam9icyRaKSkKIyB0YWJsZShkZl9qb2JzJFosIGRmX2pvYnMkbW90aXZhdGlvbikKIyBzdW1tYXJ5KGRmX2pvYnMkbW90aXZhdGlvbikKIyB1bmlxdWUoZGZfam9icyRtb3RpdmF0aW9uKQpgYGAKCgojIyMgVmlzdWFsaXphdGlvbgoKYGBge3J9CiMgT3V0Y29tZSB2YXJpYWJsZXMKb3V0Y29tZV92YXJzIDwtIGMoJ2RlcHJlc3M2JywgJ2VtcGxveTYnKQoKcGFyKG1mcm93PWMoMSwgMikpCgpmb3IgKHZhciBpbiBvdXRjb21lX3ZhcnMpIHsKICBoaXN0KGRmX2pvYnNbW3Zhcl1dLCBmcmVxPUZBTFNFLCBtYWluPXBhc3RlKHZhciwifCBwb3N0IHRyZWF0bWVudCIpLCB4bGFiPU5VTEwsIGNvbD0iI0ZDNEUwNyIpfQoKIwpvdXRjb21lX3ZhcnMgPC0gYygnZGVwcmVzczAnLCAnZGVwcmVzczYnKQoKcGFyKG1mcm93PWMoMiwgMikpCgpmb3IgKHZhciBpbiBvdXRjb21lX3ZhcnMpIHtoaXN0KGRmX2pvYnNbW3Zhcl1dW2RmX2pvYnMkWiA9PSAwXSwgZnJlcT1GQUxTRSwgbWFpbj1wYXN0ZSh2YXIsICJDb250cm9sIiwgc2VwPSIgfCAiKSwgeGxhYj1OVUxMLCBjb2w9IiNGQzRFMDciKX0KZm9yICh2YXIgaW4gb3V0Y29tZV92YXJzKSB7aGlzdChkZl9qb2JzW1t2YXJdXVtkZl9qb2JzJFogPT0gMV0sIGZyZXE9RkFMU0UsIG1haW49cGFzdGUodmFyLCAiVHJlYXQiLCBzZXA9IiB8ICIpLCB4bGFiPU5VTEwsIGNvbD0iIzAwQUZCQiIpfQoKIwpvdXRjb21lX3ZhcnMgPC0gYygnZGVwcmVzczAnLCAnZGVwcmVzczYnKQoKcGFyKG1mcm93PWMoMSwgMikpCgpmb3IgKHZhciBpbiBvdXRjb21lX3ZhcnMpIHsKICBoaXN0KGRmX2pvYnNbW3Zhcl1dLCBmcmVxPUZBTFNFLCBtYWluPXZhciwgeGxhYj1OVUxMLCBjb2w9IiNGQzRFMDciKX0KCgojIEJpbmFyeSB2YXJpYWJsZXMKYmluX3ZhcnMgPC0gYygnc2V4JywgJ3JhY2UnLCAnbm9ubWFycmllZCcpIAoKcGFyKG1mcm93PWMobGVuZ3RoKGJpbl92YXJzKSwgMykpCgoKZm9yICh2YXIgaW4gYmluX3ZhcnMpIHsKICAKICBoaXN0KGRmX2pvYnNbW3Zhcl1dW2RmX2pvYnMkWiA9PSAwXSwgeGxhYj1OVUxMLCBtYWluPXBhc3RlKHZhciwgIkNvbnRyb2wiLCBzZXA9IiB8ICIpLCBjb2w9IiNGQzRFMDciKQogIAogIGhpc3QoZGZfam9ic1tbdmFyXV1bZGZfam9icyRaID09IDFdLCB4bGFiPU5VTEwsIG1haW49cGFzdGUodmFyLCAiVHJlYXQiLCBzZXA9IiB8ICIpLCBjb2w9IiMwMEFGQkIiKQogIAogIGhpc3QoZGZfam9ic1tbdmFyXV0sIGZyZXE9RkFMU0UsIG1haW49cGFzdGUodmFyKSwgeGxhYj1OVUxMLCBjb2w9ImdyZWVuIikKICB9CgoKIyBQcmUtdHJlYXRtZW50IGNvbnRpbnVvdXMgdmFyaWFibGVzCmNvbnRfcHJlX3ZhcnMgPC0gYygnYWdlJywgJ2VkdWMnLCAnRWNvbkhhcmQnLCAnYXNzZXJ0aXZlJywgJ21vdGl2YXRpb24nLCAnZGVwcmVzczAnKQoKIyBQcmUtdHJlYXRtZW50IGNvbnRpbnVvdXMgdmFyaWFibGVzCnJvd3MgPC0gMS41CnBhcihtZnJvdz1jKDIqcm93cywgbGVuZ3RoKGNvbnRfcHJlX3ZhcnMpL3Jvd3MpKQoKZm9yICh2YXIgaW4gY29udF9wcmVfdmFycykgewogIGhpc3QoZGZfam9ic1tbdmFyXV1bZGZfam9icyRaID09IDBdLCB4bGFiPU5VTEwsIG1haW49cGFzdGUodmFyLCAiQ29udHJvbCIsIHNlcD0iIHwgIiksIGNvbD0iI0ZDNEUwNyIpCiAgCiAgaGlzdChkZl9qb2JzW1t2YXJdXVtkZl9qb2JzJFogPT0gMV0sIHhsYWI9TlVMTCwgbWFpbj1wYXN0ZSh2YXIsICJUcmVhdCIsIHNlcD0iIHwgIiksIGNvbD0iIzAwQUZCQiIpCiAgfQoKCiMgUmVzZXQgcGFyCnBhcihtZnJvdz1jKDEsMSkpCmBgYApMb29raW5nIGF0IGNvdmFyaWF0ZXMgaXQgc2VlbXMgdG8gYmUgYSBzaW1pbGFyIGRpc3RyaWJ1dGlvbiBhbW9uZyB0aGUgYXNzaWdubWVudCB0byBDb250cm9sIG9yIFRyZWF0bWVudC4gCkNvbnNpZGVyaW5nIGRlcHJlc3MwIHRoZXJlIGlzIGFuIGhpZ2hlciBkZW5zaXR5IGZvciB0aGUgbG93IHZhbHVlcyBpbiB0cmVhdG1lbnQgZ3JvdXAuCgpMb29raW5nIGF0IGRlcHJlc3MgYmVmb3JlIGFuZCBhZnRlciB0cmVhdG1lbnQsIGZvciBib3RoIGNvbnRyb2wgYW5kIHRyZWF0bWVudCBncm91cHMgdGhlcmUgaXMgYSBzaGlmdCB0byB0aGUgbG93ZXIgbGV2ZWxzIG9mIGRlcHJlc3Npb24sIHNob3dpbmcgdGhlIHBvc3NpYmlsaXR5IHRoYXQgc29tZSBvdGhlciBmYWN0b3JzIGhlbHBlZCBwZW9wbGUsIG5vdCBvbmx5IHRyZWF0bWVudCBpbiBvdXIgc3R1ZHkuCgoKCiMjIDIuIEZvciBlYWNoIHZhcmlhYmxlLCBjYWxjdWxhdGUgdGhlIG1lYW4gZm9yIHRoZSB3aG9sZSBzYW1wbGUgYW5kIHdpdGhpbiBlYWNoIHRyZWF0bWVudCBncm91cC4gCkZvciBjb250aW51b3VzIGNvdmFyaWF0ZXMsIGFsc28gcmVwb3J0IHRoZSBtZWRpYW5zLCBzdGFuZGFyZCBkZXZpYXRpb24gYW5kIHJhbmdlcyB3aXRoaW4gZWFjaCB0cmVhdG1lbnQgZ3JvdXAuIApSZWNvcmQgeW91ciByZXN1bHRzIGluIGEgdGFibGUuIApJbiBhIGZldyBzZW50ZW5jZXMsIGNvbW1lbnQgb24gd2hhdCB5b3Ugc2VlIGFuZCB3aGV0aGVyIGl0IGlzIGV4cGVjdGVkLgoKYGBge3J9CiMgaGVhZGVyIDwtIGNvbG5hbWVzKGRmX2pvYnMpCgphbGxfdmFyIDwtIGMoInNleCIsICJhZ2UiLCAicmFjZSIsICJub25tYXJyaWVkIiwgImVkdWMiLCAiRWNvbkhhcmQiLCAiYXNzZXJ0aXZlIiwgIm1vdGl2YXRpb24iLCAiZGVwcmVzczAiLCAiWiIsICJlbXBsb3k2IiwgImRlcHJlc3M2IikKCiMgTWVhbiwgc2QsIG1lZGlhbiwgcmFuZ2UgYnkgdHJlYXRtZW50IG9mIGNvbnRpbnVvdXMgdmFycwpjb250aW51b3VzX3ZhciA8LSBjKCdhZ2UnLCAnZWR1YycsICdFY29uSGFyZCcsICdhc3NlcnRpdmUnLCAnbW90aXZhdGlvbicsICdkZXByZXNzMCcsICdkZXByZXNzNicpCgoKIyBEZXNjcmlwdGl2ZSBzdGF0aXN0aWNzCkFsbF9zdGF0PC1kYXRhLmZyYW1lKCBjYmluZCgKICAgICAgYXBwbHkoZGZfam9ic1ssYWxsX3Zhcl0sIDIsbWVhbiksIAogICAgICAKICAgICAgYXBwbHkoZGZfam9ic1tkZl9qb2JzJFo9PTAsYWxsX3Zhcl0sIDIsbWVhbiksCiAgICAgIAogICAgICBhcHBseShkZl9qb2JzW2RmX2pvYnMkWj09MSxhbGxfdmFyXSwgMixtZWFuKSkpCgpjb2xuYW1lcyhBbGxfc3RhdCk8LSBjKCJNZWFuIiwgIiBNZWFuLUMiLCAiIE1lYW4tVCIpCnJvdW5kKEFsbF9zdGF0LDIpCgpDb250X3N0YXQ8LWRhdGEuZnJhbWUoIGNiaW5kKAogICAgICBhcHBseShkZl9qb2JzW2RmX2pvYnMkWj09MCxjb250aW51b3VzX3Zhcl0sIDIsIG1lZGlhbiksCiAgICAgIAogICAgICBhcHBseShkZl9qb2JzW2RmX2pvYnMkWj09MSxjb250aW51b3VzX3Zhcl0sIDIsIG1lZGlhbiksCiAgICAgIAogICAgICBhcHBseShkZl9qb2JzW2RmX2pvYnMkWj09MCxjb250aW51b3VzX3Zhcl0sIDIsIHNkKSwKICAgICAgCiAgICAgIGFwcGx5KGRmX2pvYnNbZGZfam9icyRaPT0xLGNvbnRpbnVvdXNfdmFyXSwgMiwgc2QpLAogICAgICAKICAgICAgYXBwbHkoZGZfam9ic1tkZl9qb2JzJFo9PTAsY29udGludW91c192YXJdLCAyLCBtaW4pLAogICAgICAKICAgICAgYXBwbHkoZGZfam9ic1tkZl9qb2JzJFo9PTEsY29udGludW91c192YXJdLCAyLCBtaW4pLAogICAgICAKICAgICAgYXBwbHkoZGZfam9ic1tkZl9qb2JzJFo9PTAsY29udGludW91c192YXJdLCAyLCBtYXgpLAogICAgICAKICAgICAgYXBwbHkoZGZfam9ic1tkZl9qb2JzJFo9PTEsY29udGludW91c192YXJdLCAyLCBtYXgpKSkKCmNvbG5hbWVzKENvbnRfc3RhdCk8LSBjKCIgTWVkaWFuLUMiLCAiIE1lZGlhbi1UIiwgIiBzLmQuLUMiLCAiIHMuZC4tVCIsICIgTWluLUMiLCAiIE1pbi1UIiwgIiBNYXgtQyIsICIgTWF4LVQiKQpyb3VuZChDb250X3N0YXQsMikKClN0YXQgPC0gbWVyZ2UoQWxsX3N0YXQsIENvbnRfc3RhdCwgYnk9InJvdy5uYW1lcyIsIGFsbCA9IFRSVUUpClN0YXQKYGBgCkluIHRlcm1zIG9mIHJhbmRvbWl6YXRpb24sIGNvbnNpZGVyaW5nIGZvciBleGFtcGxlIHRoZSB2YXJpYWJsZSAiYWdlIiwgdGhlIG1lYW4gdmFsdWUgYW5kIHN0YW5kYXJkIGRldmlhdGlvbiBhcmUgcXVpdGUgdGhlIHNhbWUgZm9yIHRyZWF0ZWQgYW5kIG5vbi10cmVhdGVkIGdyb3VwcywgYnV0IGl0IGhhcyBhIGxpdHRsZSBzaWduaWZpY2FudCBkaWZmZXJlbmNlIGJldHdlZW4gbWF4IGFnZSwgNjkgdnMgNjEuIEFsc28gYnkgc2V4LCB0aGVyZSdzIGEgNyAlIGRpc2NhcmQgYmV0d2VlbiB0aGUgdHdvIGdyb3VwcywgYWxvbmcgd2l0aCBvdGhlciBub3Qtc28tc2ltaWxhciBkaXN0cmlidXRpb25zIGxpa2UgcmFjZSwgbWFyaXRhbCBzdGF0dXMuIENvdWxkIHRoaXMgbWF5YmUgYWZmZWN0IHRoZSByZXN1bHRzPwoKVGhlIGRlcHJlc3Npb24gbGV2ZWwgbG9va3MgYSBsaXR0bGUgbG93ZXIgaW4gdGhlIHRyZWF0bWVudCBncm91cCwgdGhhdCBjb3VsZCBiZSB0YWtlbiBpbnRvIGFjY291bnQgY29uc2lkZXJpbmcgdGhlIG91dGNvbWUgImRlcHJlc3M2Ii4KSW4gYm90aCBjYXNlcyB0aGVyZSBpcyBhIGRyb3AgZG93biBpbiB0aGUgbWVhbiB2YWx1ZSBvZiBkZXByZXNzaW9uIGZyb20gcHJlIHRvIHBvc3QgdHJlYXRtZW50IHRpbWUuCgpJbiB0ZXJtcyBvZiBlbXBsb3ltZW50IGF0IHRoZSBlbmQgb2YgdGhlIDYgbW9udGhzLCB0aGVyZSdzIGEgZGlmZmVyZW5jZSBpbiB0ZXJtcyBvZiBtZWFuIHZhbHVlLCBidXQgSSBoYXZlIG5vIHByZS10cmVhdG1lbnQgdmFsdWVzIHRvIGRvIGFueSBjb25zaWRlcmF0aW9uIGFib3V0LiBTcGVjdWxhdGluZyB3aXRoIEVjb25vbWljIEhhcmRuZXNzIGl0IHNlZW1zIHRvIHVuZGVybGluZSB0aGUgZWZmZWN0IG9mIHRoZSB0cmVhdG1lbnQgaW4gZW1wbG95bWVudCBzdGF0dXMuCgoKIyMgMy4gRmlzaGVyIGV4YWN0IHDiiJJ2YWx1ZSBhcHByb2FjaC4gT3V0Y29tZSB2YXJpYWJsZTog4oCcZGVwcmVzc2lvbiBzaXggbW9udGhzIGFmdGVyIHRoZSBpbnRlcnZlbnRpb24gYXNzaWdubWVudC7igJ0KYGBge3J9Ck4gIDwtIG5yb3coZGZfam9icykgICAgICAgICMgdG90YWwgc2FtcGxlIHNpemUKTnQgPC0gc3VtKGRmX2pvYnMkWj09MSkgICAgIyBudW1iZXIgb2YgdHJlYXRlZCB1bml0cwpOYyA8LSBzdW0oZGZfam9icyRaPT0wKSAgICAjIG51bWJlciBvZiBjb250cm9scwoKYyhOLCBOdCwgTmMpCmBgYAoKCmBgYHtyfQojIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIwojIyBGaXNoZXIncyBFeGFjdCBwLXZhbHVlCiMjLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0jCgojIyBTaGFycCBudWxsIGh5cG90aGVzaXMgdGhhdCB0aGUgdHJlYXRtZW50IGhhZCBubyBlZmZlY3QgCiMjIEgwOiBZaSgxKT1ZaSgwKSBmb3IgaT0xLC4uLixOCgojIyBDb25zaWRlcmluZyBvdXRjb21lIHZhcmlhYmxlOiDigJxkZXByZXNzaW9uIHNpeCBtb250aHMgYWZ0ZXIgdGhlIGludGVydmVudGlvbiBhc3NpZ25tZW504oCdCgoKIyBwb3NzaWJsZSBjb21iaW5hdGlvbnMKbmFzcyA8LSBjaG9vc2UoTixOdCkgIyBudW1iZXIgb2YgYXNzaWdubWVudCB2ZWN0b3JzCm5hc3MgIyB3ZSBzaG91bGQgY29tcHV0ZSB0aGUgYXZlcmFnZSB0cmVhdG1lbnQgZWZmZWN0IGZvciBlYWNoIG9mIHRoZW0uLi4KCmBgYAoKIyMjIChhKSBBcHByb3hpbWF0ZSB1c2luZyA1MDAwIGRyYXdzIGZyb20gdGhlIHJhbmRvbWl6YXRpb24gZGlzdHJpYnV0aW9uLCB0aGUgZXhhY3QgRmlzaGVyIHDiiJJ2YWx1ZXMgZm9yIGEgc2hhcnAgbnVsbCBoeXBvdGhlc2lzIG9mIHplcm8gdHJlYXRtZW50IGVmZmVjdHMgdXNpbmcgdGhlIGZvbGxvd2luZyB0d28gc3RhdGlzdGljczogCkFic29sdXRlIHZhbHVlIG9mIHRoZSBkaWZmZXJlbmNlIGluIGF2ZXJhZ2Ugb3V0Y29tZXMgYnkgdHJlYXRtZW50IHN0YXR1cwpBYnNvbHV0ZSB2YWx1ZSBvZiB0aGUgZGlmZmVyZW5jZSBpbiBhdmVyYWdlIHJhbmtzIGJ5IHRyZWF0bWVudCBzdGF0dXMuCmBgYHtyfQojIEFic29sdXRlIHZhbHVlIG9mIHRoZSBkaWZmZXJlbmNlIGluIGF2ZXJhZ2Ugb3V0Y29tZXMgYnkgdHJlYXRtZW50IHN0YXR1czoKZGlmLmF2ZS5vYnMgPC0gbWVhbihkZl9qb2JzJGRlcHJlc3M2W2RmX2pvYnMkWj09MV0pIC0gbWVhbihkZl9qb2JzJGRlcHJlc3M2W2RmX2pvYnMkWj09MF0pCmRpZi5hdmUub2JzCgpUb2JzLmRpZi5hdmUgIDwtIGFicyhkaWYuYXZlLm9icykKVG9icy5kaWYuYXZlCmBgYAoKCmBgYHtyfQojIFRoaXMgZnVuY3Rpb24gYXNzaWducyByYW5rcyB0byB0aGUgdmFsdWVzLCBhbmQgdGhlIHRpZXMubWV0aG9kID0gImF2ZXJhZ2UiIGFyZ3VtZW50IHNwZWNpZmllcyBob3cgdG8gaGFuZGxlIHRpZWQgdmFsdWVzLiBXaGVuIHRoZXJlIGFyZSB0aWVkIHZhbHVlcyAodmFsdWVzIHRoYXQgYXJlIHRoZSBzYW1lKSwgdGhlICJhdmVyYWdlIiBtZXRob2QgYXNzaWducyB0aGUgYXZlcmFnZSBvZiB0aGUgcmFua3MgdG8gdGhvc2UgdGllZCB2YWx1ZXMuCiMgVGhlICdyJyB2YXJpYWJsZSB3aWxsIG5vdyBjb250YWluIHRoZSByYW5rcyBjb3JyZXNwb25kaW5nIHRvIHRoZSBvcmlnaW5hbCB2YWx1ZXMgaW4gJ2RlcHJlc3M2Jy4gVGhpcyBraW5kIG9mIHRyYW5zZm9ybWF0aW9uIGlzIG9mdGVuIHVzZWQgaW4gbm9uLXBhcmFtZXRyaWMgc3RhdGlzdGljcyBvciB3aGVuIHRoZSBhc3N1bXB0aW9ucyBvZiBub3JtYWxpdHkgYXJlIG5vdCBtZXQuCgpyIDwtIHJhbmsoZGZfam9icyRkZXByZXNzNiwgdGllcy5tZXRob2QgPSAiYXZlcmFnZSIpCgpkaWYub2JzLnIgPC0gbWVhbihyW2RmX2pvYnMkWj09MV0pIC0gbWVhbihyW2RmX2pvYnMkWj09MF0pCmRpZi5vYnMucgoKVG9icy5kaWYuciA8LSBhYnMoZGlmLm9icy5yKQpUb2JzLmRpZi5yCmBgYAoKCmBgYHtyfQojIFdlIHRlc3QgdGhlIFNoYXJwIG51bGwgYWdhaW5zdCAtIHNvIHBvdGVudGlhbCBvdXRjb21lcyB3aXRoIG9yIHdpdGhvdXQgdHJlYXRtZW50IGFyZSB0aGUgc2FtZSwgYW5kIHdlIGNhbiBjb25zdHJ1Y3QgdGhlbQoKIyBmaXggYSBzZWVkIGZvciByZXByb2R1Y2liaWxpdHkKc2V0LnNlZWQoMjMpCgojIFAtdmFsdWVzIGVzdGltYXRlZCB1c2luZyBLIGRyYXdzIGZyb20gdGhlIHJhbmRvbWl6YXRpb24gZGlzdHJpYnV0aW9uOgpLIDwtIDUwMDAgCgpwLmF2ZSA8LSBwLnIgPC0gMCAgIyBQLnZhbHVlClRkaWYuYXZlLmRpc3QgPC0gVGRpZi5kaXN0LnIgPC0gTlVMTCAgIyBpbml0aWFsaXppbmcgdmVjdG9ycwoKCiMgYXQgZXZlcnkgaXRlcmF0aW9uIHNhbXBsZSBhIE4gZGltZW50aW9uIHZlY3RvciBpbiBxCmZvcihrIGluIDE6Syl7CiAgWi5zaW0gPC0gc2FtcGxlKGRmX2pvYnMkWiwgTiwgcmVwbGFjZT1GQUxTRSkgICMgc2ltdWxhdGlvbiwgZG9lc24ndCBtZXR0ZXIgaWYgd2l0aCByZXBsYWNlbWVudCBvciBub3QKICAKICBkaWYuYXZlIDwtIG1lYW4oZGZfam9icyRkZXByZXNzNltaLnNpbT09MV0pIC0gbWVhbihkZl9qb2JzJGRlcHJlc3M2W1ouc2ltPT0wXSkgIyBtZWFuIGRpZmYKICBUZGlmLmF2ZSA8LSBhYnMoZGlmLmF2ZSkgICMgYWJzIHZhbHVlCiAgVGRpZi5hdmUuZGlzdCA8LSBjKFRkaWYuYXZlLmRpc3QsIFRkaWYuYXZlKSAgIyBBcHBlbmRzIFRkaWYuYXZlIHRvIFRkaWYuYXZlLmRpc3QgdG8ga2VlcCB0cmFjayBvZiBkaXN0cmlidXRpb24gdW5kZXIgbnVsbCBoeXBvdGhlc2lzCiAgcC5hdmUgPC0gcC5hdmUgKyAxKihUZGlmLmF2ZT49VG9icy5kaWYuYXZlKSAgIyBVcGRhdGVzIGEgY291bnRlciBpZiBvYnNlcnZlZCBtZWFuIGRpZmZlcmVuY2UgaXMgZ3JlYXRlciB0aGFuIHRvIHNpbXVsYXRlZCBtZWFuIGRpZmZlcmVuY2VzCiAgCiAgIyBSYW5rCiAgZGlmLnIgPC0gbWVhbihyW1ouc2ltPT0xXSkgLSBtZWFuKHJbWi5zaW09PTBdKQogIFRkaWYuciA8LSBhYnMoZGlmLnIpCiAgVGRpZi5kaXN0LnIgPC0gYyhUZGlmLmRpc3QuciwgVGRpZi5yKQogIHAuciA8LSBwLnIgKyAxKihUZGlmLnIgPj0gVG9icy5kaWYucikKICB9CgpwLmF2ZSA8LSBwLmF2ZS9LCnAuciAgIDwtIHAuci9LCgpjKFRvYnMuZGlmLmF2ZSwgVG9icy5kaWYuciwgcC5hdmUsIHAucikKCmBgYApVbmRlciB0aGUgbnVsbCBoeXBvdGhlc2lzIG9mIG5vIGVmZmVjdCBvZiB0aGUgcHJvZ3JhbSwgaGF2aW5nIGEgcC12YWx1ZSBvZiAwLjA5IGFuZCAwLjA4IG1ha2Ugbm8gc3VmZmljaWVudCBldmlkZW5jZSB0byBzdHJvbmdseSByZWplY3QgdGhlIG51bGwgaHlwb3RoZXNpcy4KCgpgYGB7cn0KcGFyKG1mcm93PWMoMSwyKSkKCmhpc3QoVGRpZi5hdmUuZGlzdCwgZnJlcT1UUlVFLCBtYWluPSJBYnMgZGlmZiBpbiBhdmVyYWdlIG91dGNvbWVzIiwKICAgICBicmVha3M9MTAwLAogICAgIHhsYWI9TlVMTCkKCmFibGluZSh2PVRvYnMuZGlmLmF2ZSwgY29sPSJmb3Jlc3RncmVlbiIsbHdkPTIpIAoKCmhpc3QoVGRpZi5kaXN0LnIsIGZyZXE9VFJVRSwgbWFpbj0iQWJzIGRpZmYgaW4gYXZlcmFnZSByYW5rcyIsCiAgICAgYnJlYWtzPTEwMCwKICAgICB4bGFiPU5VTEwpCgphYmxpbmUodj1Ub2JzLmRpZi5yLCBjb2w9ImZvcmVzdGdyZWVuIixsd2Q9MikgCmBgYAoKCiMjIyAoYikgQ2FsY3VsYXRlIGEgOTAlIEZpc2hlciBpbnRlcnZhbCBmb3IgYSBjb25zdGFudCBhZGRpdGl2ZSB0cmVhdG1lbnQgZWZmZWN0IHVzaW5nIHRoZSBhYnNvbHV0ZSB2YWx1ZSBvZiB0aGUgZGlmZmVyZW5jZSBpbiBhdmVyYWdlIG91dC0gY29tZXMgYnkgdHJlYXRtZW50IHN0YXR1cyBhcyB0ZXN0IHN0YXRpc3RpYy4KCmBgYHtyfQojIyMgSW50ZXJ2YWwgZXN0aW1hdGVzIGJhc2VkIG9uIEZFUCAoc2ltdWxhdGVkIHAtdmFsdWVzKQojIEZpc2hlciBpbnRlcnZhbCBmb3IgYSBjb21tb24gYWRkaXRpdmUgZWZmZWN0CiMgSDA6IFkoMSkgPSBZKDApICsgdGF1Cgp0YXUgPC0gc2VxKC0uNSwgLjUsIGJ5PS4wNSkKcC5kaWYgPC0gcmVwKDAsIGxlbmd0aCh0YXUpKQpUb2JzLmRpZiA8LSBOVUxMCgoKZm9yKGsgaW4gMTpLKXsKICBaLnNpbSA8LSBzYW1wbGUoZGZfam9icyRaLCBOLCByZXBsYWNlPUZBTFNFKQogIAogIGZvcihqIGluIDE6bGVuZ3RoKHRhdSkpewogICAgCiAgICAjSW1wdXRlZCBkZl9qb2JzIHVuZGVyIHRoZSBudWxsIGh5cG90aGVzaXMKICAgIFkwIDwtIGRmX2pvYnMkZGVwcmVzczYqKGRmX2pvYnMkWj09MCkgKyAoZGZfam9icyRkZXByZXNzNi10YXVbal0pKihkZl9qb2JzJFo9PTEpCiAgICBZMSA8LSBkZl9qb2JzJGRlcHJlc3M2KihkZl9qb2JzJFo9PTEpICsgKGRmX2pvYnMkZGVwcmVzczYrdGF1W2pdKSooZGZfam9icyRaPT0wKQogICAgCiAgICBUb2JzLmRpZltqXSA8LSBhYnMobWVhbihkZl9qb2JzJGRlcHJlc3M2W2RmX2pvYnMkWj09MV0pIC0gbWVhbihkZl9qb2JzJGRlcHJlc3M2W2RmX2pvYnMkWj09MF0pIC0gdGF1W2pdKQogICAgCiAgICBUZGlmIDwtIGFicyhtZWFuKFkxW1ouc2ltPT0xXSkgLSBtZWFuKFkwW1ouc2ltPT0wXSkgLSB0YXVbal0pCiAgICAKICAgIHAuZGlmW2pdPC0gcC5kaWZbal0gKyAxICogKFRkaWY+PVRvYnMuZGlmW2pdKQogICAgfQogICAgfQoKcC5kaWY8LSBwLmRpZi9LCgpGQ0k8LWNiaW5kKHRhdSwgVG9icy5kaWYsIHAuZGlmKQpGQ0kKYGBgCkNvbmZpZGVuY2UgaW50ZXJ2YWw6IEkgc2VlayBhIDkwJSBGaXNoZXIgaW50ZXJ2YWwgZm9yIGEgY29uc3RhbnQgYWRkaXRpdmUgdHJlYXRtZW50LiBDb25zaWRlcmluZyB0aGUgdGVzdCBzdGF0aXN0aWNzIHBlcmZvcm1lZCwgaXQncyBub3Qgc28gZXh0cmVtZSB0byByZWplY3RlZCB0aGUgbnVsbCBoeXBvdGhlc2lzIGluIHRoZSBpbnRlcnZhbCB3aGVyZSBwLXZhbHVlcyBhcmUgZ3JlYXRlciB0aGFuIDAuMSwgc28gdGhlIGNvcnJlc3BvbmRpbmcgdGF1cyByYW5nZSBpcyBbLTAuMjUsIC0wLjA1XS4KCgoKIyMgNC4gTmV5bWFu4oCZcyBSZXBlYXRlZCBTYW1wbGluZyBBcHByb2FjaC4gT3V0Y29tZSB2YXJpYWJsZTog4oCcZGVwcmVzc2lvbiBzaXggbW9udGhzIGFmdGVyIHRoZSBpbnRlcnZlbnRpb24gYXNzaWdubWVudC7igJ0KIyMjIChhKSBDYWxjdWxhdGUgYW4gdW5iaWFzZWQgZXN0aW1hdGUgb2YgdGhlIGF2ZXJhZ2UgdHJlYXRtZW50IGVmZmVjdC4KCmBgYHtyfQojIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIwojIyBOZXltYW4ncyBSZXBlYXRlZCBTYW1wbGluZyBBcHByb2FjaAojIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIwoKWW9icyA8LSBkZl9qb2JzJGRlcHJlc3M2ClogICAgPC0gZGZfam9icyRaCgp0YXUuZGlmPC0gbWVhbihZb2JzW1o9PTFdKSAtIG1lYW4oWW9ic1taPT0wXSkKdGF1LmRpZgpgYGAKQXZhcmFnZSB0cmVhdG1lbnQgZWZmZWN0LgoKCmBgYHtyfQojIEVzdGltYXRlcyBvZiB0aGUgc2FtcGxlIHZhcmlhbmNlIG9mIHRoZSBwb3RlbnRpYWwgY29udHJvbCBvdXRjb21lCnMyLmM8LSB2YXIoWW9ic1taPT0wXSkKczIuYwpgYGAKCgpgYGB7cn0KIyBFc3RpbWF0ZXMgb2YgdGhlIHNhbXBsZSB2YXJpYW5jZSBvZiB0aGUgcG90ZW50aWFsIHRyZWF0ZWQgb3V0Y29tZQpzMi50PC0gdmFyKFlvYnNbWj09MV0pCnMyLnQKYGBgCgoKYGBge3J9CiMjIEVzdGltYXRlcyBvZiB0aGUgdmFyaWFuY2Ugb2YgdGhlIHRyZWF0bWVudCBlZmZlY3QgZXN0aW1hdG9ycwpWbmV5bWFuIDwtIHMyLmMvTmMgKyBzMi50L050ClZuZXltYW4KYGBgCgoKKGIpIEFwcGx5IE5leW1hbuKAmXMgbWV0aG9kIHRvIGNvbnN0cnVjdCBhIDkwJSBsYXJnZSBzYW1wbGUgY29uZmlkZW5jZSBpbnRlcnZhbCBmb3IgdGhlIGF2ZXJhZ2UgdHJlYXRtZW50IGVmZmVjdAoKYGBge3J9CiMjIENvbmZpZGVuY2UgaW50ZXJ2YWxzOiAKIyAgMS1hbHBoYSA9IDAuOSwgc28gMSAtIChhbHBoYS8yKSA9IDAuOTUKWCA8LSBxbm9ybSgwLjk1KQoKYyh0YXUuZGlmIC0gWCAqIHNxcnQoVm5leW1hbiksIHRhdS5kaWYgKyBYICogc3FydChWbmV5bWFuKSkKYGBgClRoZSBjb25maWRlbmNlIGludGVydmFsIGZvdW5kIGlzIHNpbWlsYXIgdG8gdGhlIG9uZSBjb21wdXRlZCBieSBGaXNoZXIgKGFib3ZlKS4KClRoZSBuZWdhdGl2ZSB2YWx1ZXMgaW4gdGhlIGludGVydmFsIGluZGljYXRlIHRoYXQgdGhlcmUgaXMgYSBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50IGRlY3JlYXNlIGluIHRoZSBvdXRjb21lIHZhcmlhYmxlIGFzc29jaWF0ZWQgd2l0aCB0aGUgdHJlYXRtZW50IGJhc2VkIG9uIE5leW1hbidzIG1ldGhvZC4KCgpgYGB7cn0KIyMgVGVzdGluZwojIyBIeXBvdGhlc2VzOiAgCiMgIEgwOiB0YXUuZGlmICA9IDAgdnMgCiMgIEgxOiB0YXUuZGlmICE9IDAKdG5leW1hbiA8LSAodGF1LmRpZi0wKS9zcXJ0KFZuZXltYW4pICAjIHRlc3Qgc3RhdGlzdGljIGZvciB0aGUgTmV5bWFuIHRlc3QKdG5leW1hbgpgYGAKCgpgYGB7cn0KIyBwLXZhbHVlIGJhc2VkIG9uIHRoZSBub3JtYWwgYXBwcm94aW1hdGlvbgoyKigxLXBub3JtKGFicyh0bmV5bWFuKSkpICAjIGdpdmVzIHRoZSBjdW11bGF0aXZlIGRpc3RyaWJ1dGlvbiBmdW5jdGlvbiAoQ0RGKSBvZiB0aGUgc3RhbmRhcmQgbm9ybWFsIGRpc3RyaWJ1dGlvbgpgYGAKR2l2ZW4gYSBwLXZhbHVlIG9mIDAuMDk5Mzg2MzEsIGl0IHN1Z2dlc3RzIHRoYXQgdGhlIG9ic2VydmVkIGVzdGltYXRlIGlzIG5vdCBzaWduaWZpY2FudGx5IGRpZmZlcmVudCBmcm9tIHplcm8gYXQgYSBjb252ZW50aW9uYWwgc2lnbmlmaWNhbmNlIGxldmVsIG9mIDAuMDUuIEhvd2V2ZXIsIHdlJ3JlIGludGVyZXN0ZWQgaW4gY29uc3RydWN0aW5nIGEgOTAlIGNvbmZpZGVuY2UgaW50ZXJ2YWwsIHRoZSBmYWN0IHRoYXQgdGhlIGludGVydmFsIGRvZXMgbm90IGluY2x1ZGUgemVybyB3b3VsZCBzdWdnZXN0IHRoYXQgd2UgbWlnaHQgc2xpZ2h0bHkgcmVqZWN0IHRoZSBudWxsIGh5cG90aGVzaXMgaW4gZmF2b3Igb2YgYWx0ZXJuYXRpdmUgb25lIG9mIG5vbi16ZXJvLWVmZmVjdC4KCgojIyA1LiBCYXllc2lhbiBtb2RlbC1iYXNlZCBhbmFseXNpcy4gQmF5ZXNpYW4gbW9kZWwtYmFzZWQgYW5hbHlzaXMgZm9yIHRoZSBvdXRjb21lIHZhcmlhYmxlIOKAnGRlcHJlc3Npb24gc2l4IG1vbnRocyBhZnRlciB0aGUgaW50ZXJ2ZW50aW9uIGFzc2lnbm1lbnQu4oCdIEFzc3VtZSB0aGF0IFlpKDApIGFuZCBZaSgxKSBhcmUgaW5kZXBlbmRlbnQgYW5kIGFyZSBib3RoIGxvZy1ub3JtYWxseSBkaXN0cmlidXRlZC4KCkRlcml2ZSB0aGUgcG9zdGVyaW9yIGRpc3RyaWJ1dGlvbnMgb2YgdGhlIGZpbml0ZSBzYW1wbGUgYXZlcmFnZSBjYXVzYWwgZWZmZWN0IGFuZCB0aGUgc3VwZXItcG9wdWxhdGlvbiBhdmVyYWdlIGNhdXNhbCBlZmZlY3QuIApQbG90IHRoZSByZXN1bHRpbmcgcG9zdGVyaW9yIGRpc3RyaWJ1dGlvbnMgaW4gYSBoaXN0b2dyYW0gYW5kIHJlcG9ydCB0aGUgZm9sbG93aW5nIHN1bW1hcnkgc3RhdGlzdGljcyBvZiB0aGUgcmVzdWx0aW5nIHBvc3RlcmlvciBkaXN0cmlidXRpb25zOiBtZWFuLCBzdGFuZGFyZCBkZXZpYXRpb24sIG1lZGlhbiwgMi41JSBhbmQgOTcuNSUgcGVyY2VudGlsZXMuCgpgYGB7cn0KbWNtYy5tNSA8LSBmdW5jdGlvbihuaXRlciwgbmJ1cm4sIHRoaW49MSwgICAKICAgICAgICAgICAgICAgICAgICBwYXIucHJpb3IsCiAgICAgICAgICAgICAgICAgICAgT3V0Y29tZS5vYnMsIFcsCiAgICAgICAgICAgICAgICAgICAgc2VlZD1OVUxMLCB0aGV0YS5zdGFydD1OVUxMLCBjcmVkLmxldmVsPTAuOTUpeyAKICAKICAgIFlvYnMgPC0gbG9nKE91dGNvbWUub2JzKSAgIyBsb2dhcml0aG0gdHJhbnNmb3JtYXRpb24KICAgIAogICAgTmM8LSBzdW0oMS1XKTsgTnQ8LSBzdW0oVyk7IE48LSBOdCtOYwogICAgeW9icy5jPC1tZWFuKFlvYnNbVz09MF0pOyB5b2JzLnQ8LW1lYW4oWW9ic1tXPT0xXSkKICAgIAogICAgZHJhd3M8LSBzZXEoKG5idXJuKzEpLCBuaXRlciwgYnk9dGhpbikKICAgIG5kcmF3czwtIGxlbmd0aChkcmF3cykKICAgIGogPC0gMCAjIENvdW50ZXIgaj0xLi4ubmRyYXdzCiAgICAKICAgICMgU3RhcnQgdmFsdWVzCiAgICBpZihpcy5udWxsKHRoZXRhLnN0YXJ0KT09VFJVRSl7CgogICAgdGhldGEgPC0gbGlzdChiZXRhLmMgID0gbWVhbihZb2JzW1c9PTBdKSArIHJub3JtKDEsMCwgMC4xKSwKICAgICAgICAgICAgICAgICAgYmV0YS50ICA9IG1lYW4oWW9ic1tXPT0xXSkgKyBybm9ybSgxLDAsIDAuMSksCiAgICAgICAgICAgICAgICAgIHNpZ21hMi5jID0gdmFyKFlvYnNbVz09MF0pICsgcm5vcm0oMSwwLCAwLjAxKSwKICAgICAgICAgICAgICAgICAgc2lnbWEyLnQgPSB2YXIoWW9ic1tXPT0xXSkgKyBybm9ybSgxLDAsIDAuMDEpCiAgICAgICAgICAgICAgICAgICkKCiAgICB9ZWxzZXt0aGV0YTwtIHRoZXRhLnN0YXJ0fQogIAogIAogICAgVGhldGEgPC0gbWF0cml4KE5BLCBuZHJhd3MsICBsZW5ndGgodW5saXN0KHRoZXRhKSkgKQogICAgY29sbmFtZXMoVGhldGEpIDwtIG5hbWVzKHRoZXRhKQogICAgCiAgICBFc3RpbWFuZHM8LSBtYXRyaXgoTkEsIG5kcmF3cywgMikKICAgIGNvbG5hbWVzKEVzdGltYW5kcyk8LSBjKCJhdGUuZnMiLCAiYXRlLnNwIikKICAgIAogICAgaWYoaXMubnVsbChzZWVkKT09RkFMU0UpewogICAgICBzZXQuc2VlZChzZWVkKX0KCiAgICBmb3IobCBpbiAxOm5pdGVyKXsKICAgIAogICAgIyNVcGRhdGUgYmV0YS5jCiAgICB0YXUyLmMub2JzICAgPC0gMS97TmMvdGhldGEkc2lnbWEyLmMgKyAxL3Bhci5wcmlvciR0YXUyLmN9CiAgICBudS5jLm9icyAgICAgPC0gdGF1Mi5jLm9icyp7KHlvYnMuYypOYykvdGhldGEkc2lnbWEyLmMgKyBwYXIucHJpb3IkbnUuYy9wYXIucHJpb3IkdGF1Mi5jfQogICAgdGhldGEkYmV0YS5jIDwtIHJub3JtKDEsIG51LmMub2JzLCBzcXJ0KHRhdTIuYy5vYnMpKQogICAgCiAgICAjI1VwZGF0ZSBiZXRhLnQKICAgIHRhdTIudC5vYnMgICA8LSAxL3tOdC90aGV0YSRzaWdtYTIudCsgMS9wYXIucHJpb3IkdGF1Mi50fQogICAgbnUudC5vYnMgICAgIDwtIHRhdTIudC5vYnMqeyh5b2JzLnQqTnQpL3RoZXRhJHNpZ21hMi50ICsgcGFyLnByaW9yJG51LnQvcGFyLnByaW9yJHRhdTIudH0KICAgIHRoZXRhJGJldGEudCA8LSBybm9ybSgxLCBudS50Lm9icywgc3FydCh0YXUyLnQub2JzKSkKICAgIAogICAgIyNVcGRhdGUgc2lnbWEyLmMKICAgIGEuYy5vYnMgICAgICAgIDwtIE5jICsgcGFyLnByaW9yJGEuYwogICAgYjIuYy5vYnMgICAgICAgPC0gIHtwYXIucHJpb3IkYS5jKnBhci5wcmlvciRiMi5jICsgc3VtKHtZb2JzW1c9PTBdLXRoZXRhJGJldGEuY31eMil9L2EuYy5vYnMKICAgIHRoZXRhJHNpZ21hMi5jIDwtIHthLmMub2JzKmIyLmMub2JzfS9yY2hpc3EoMSwgYS5jLm9icykKICAgIAogICAgIyNVcGRhdGUgc2lnbWEyLnQKICAgIGEudC5vYnMgICAgICAgIDwtIE50ICsgcGFyLnByaW9yJGEudAogICAgYjIudC5vYnMgICAgICAgPC0gIHtwYXIucHJpb3IkYS50KnBhci5wcmlvciRiMi50ICsgc3VtKHtZb2JzW1c9PTFdLXRoZXRhJGJldGEudH1eMil9L2EudC5vYnMKICAgIHRoZXRhJHNpZ21hMi50IDwtICB7YS50Lm9icypiMi50Lm9ic30vcmNoaXNxKDEsIGEudC5vYnMpICAgICAgCiAgICAKICAgIHJtKHRhdTIuYy5vYnMsIG51LmMub2JzLHRhdTIudC5vYnMsIG51LnQub2JzLCBhLmMub2JzLCBiMi5jLm9icywgYS50Lm9icywgYjIudC5vYnMpCiAgICAKICAgIGlmKHN1bShsID09IGRyYXdzKT09MSl7CiAgICAgIGogPC0gaisxCiAgICAgIAogICAgICBUaGV0YVtqLF08LSB1bmxpc3QodGhldGEpCiAgICAgIAogICAgICAjIEltcHV0YXRlIHRoZSBtaXNzaW5nIHBvdGVudGlhbCBvdXRjb21lcyB1c2luZyBZbWlzIHwgWW9icywgVywgWCwgdGhldGEKICAgICAgWTA8LVkxPC1OVUxMCiAgICAgIAogICAgICBZMFtXPT0wXTwtIE91dGNvbWUub2JzW1c9PTBdICMgdmFsdWVzIHdpdGggbm8gbG9nIHRyYW5zZgogICAgICBZMFtXPT0xXTwtIGV4cChybm9ybShOdCwgdGhldGEkYmV0YS5jLCBzcXJ0KHRoZXRhJHNpZ21hMi5jKSkpICMgaW52ZXJzZSB0cmFuc2YKICAgICAgCiAgICAgIFkxW1c9PTBdPC0gZXhwKHJub3JtKE5jLCB0aGV0YSRiZXRhLnQsIHNxcnQodGhldGEkc2lnbWEyLnQpKSkgIyBpbnZlcnNlIHRyYW5zZgogICAgICBZMVtXPT0xXTwtIE91dGNvbWUub2JzW1c9PTFdICMgdmFsdWVzIHdpdGggbm8gbG9nIHRyYW5zZgogICAgICAKICAgICAgRXN0aW1hbmRzW2osImF0ZS5mcyJdIDwtIG1lYW4oWTEpIC0gbWVhbihZMCkKICAgICAgRXN0aW1hbmRzW2osImF0ZS5zcCJdIDwtIGV4cCh0aGV0YSRiZXRhLnQgKyAwLjUqdGhldGEkc2lnbWEyLnQpLWV4cCh0aGV0YSRiZXRhLmMgKzAuNSp0aGV0YSRzaWdtYTIuYykgIyBISU5UCiAgICB9CiAgfQogIAogICMgU2ltIHBvc3RlcmlvciBkaXN0cmliIG9mIEFURS5GUyBhbmQgQVRFLlNQCiAgcHJvYnM8LWMoKDEtY3JlZC5sZXZlbCkvMiwxLSgxLWNyZWQubGV2ZWwpLzIpICAjIGhpbnQKICAKICBlc3Q8LXJvdW5kKGNiaW5kKGFwcGx5KEVzdGltYW5kcywyLG1lYW4pLAogICAgICAgICAgICAgICAgICAgYXBwbHkoRXN0aW1hbmRzLDIsc2QpLAogICAgICAgICAgICAgICAgICAgYXBwbHkoRXN0aW1hbmRzLDIsbWVkaWFuKSwKICAgICAgICAgICAgICAgICAgIGFwcGx5KEVzdGltYW5kcywyLGZ1bmN0aW9uKHgpIHF1YW50aWxlKHgscHJvYnNbMV0pKSwKICAgICAgICAgICAgICAgICAgIGFwcGx5KEVzdGltYW5kcywyLGZ1bmN0aW9uKHgpIHF1YW50aWxlKHgscHJvYnNbMl0pKSksNCkKICBjb2xuYW1lcyhlc3QpPC1jKCJNZWFuIiwiIHNkIiwiIE1lZGlhbiIsIiBDSSBsb3ciLCIgQ0kgdXAiKQogIHByaW50KGVzdCkKICAKICBwYXJtczwtcm91bmQoY2JpbmQoYXBwbHkoVGhldGEsMixtZWFuKSwgIAogICAgICAgICAgICAgICAgICAgICBhcHBseShUaGV0YSwyLHNkKSksNCkKICBjb2xuYW1lcyhwYXJtcyk8LWMoIk1lYW4iLCIgc2QiKQogIHByaW50KHBhcm1zKQogIAogIHJldHVybihsaXN0KFRoZXRhPVRoZXRhLCBFc3RpbWFuZHM9RXN0aW1hbmRzKSl9CgoKcGFyLnByaW9yIDwtIGxpc3QobnUuYz0wLCBudS50PTAsIHRhdTIuYz0xMDBeMiwgdGF1Mi50PTEwMF4yLCBhLmM9MiwgYjIuYz0wLjAxLCBhLnQ9MiwgYjIudD0wLjAxKQoKCmNoYWluLm1CPC1tY21jLm01KG5pdGVyPTI1MDAwLCBuYnVybj01MDAwLCB0aGluPTEsICBwYXIucHJpb3IsIAogICAgICAgICAgICAgICAgICBPdXRjb21lLm9icz1kZl9qb2JzJGRlcHJlc3M2LCBXPWRmX2pvYnMkWiwgc2VlZD0yMDIyLCB0aGV0YS5zdGFydD1OVUxMKQoKCiMjIE92ZXJsYXBwaW5nIGhpc3RvZ3JhbXMgb2YgdGhlIHNpbXVsYXRlZCBwb3N0ZXJpb3IgZGlzdHJpYnV0aW9uIG9mIEFURS5GUwpoaXN0KGNoYWluLm1CJEVzdGltYW5kc1ssImF0ZS5mcyJdLCBmcmVxPUZBTFNFLCBicmVha3MgPSAyMCwKICAgICBtYWluID0gIkF2ZXJhZ2UgVHJlYXRtZW50IEVmZmVjdCIsIAogICAgIHhsYWI9IiIseWxhYj0iIiwgCiAgICAgY2V4LmxhYj0yLjUsIGRlbnNpdHk9MzAsIGNvbD0iI0ZDNEUwNyIpCgpoaXN0KGNoYWluLm1CJEVzdGltYW5kc1ssImF0ZS5zcCJdLCBmcmVxPUZBTFNFLCBicmVha3M9MjAsYWRkPVRSVUUsIGNvbD0iIzAwQUZCQiIsIGRlbnNpdHk9MjApCmxlZ2VuZCgidG9wcmlnaHQiLCBjZXg9YygwLjgsMC44LDAuOCksIGx0eT1jKDEsMSwxKSwgY29sPSBjKCIjRkM0RTA3IiwgIiMwMEFGQkIiKSwgbGVnZW5kPWMoIkFURS5GUyIsIkFURS5TUCIpKQpgYGAKCgojIyA2LiAoYSkgQmF5ZXNpYW4gbW9kZWwtYmFzZWQgYW5hbHlzaXMgd2l0aCBjb3ZhcmlhdGVzLiAKIyMjIEJheWVzaWFuIG1vZGVsLWJhc2VkIGFuYWx5c2lzIHdpdGggY292YXJpYXRlcyBmb3IgdGhlIG91dGNvbWUgdmFyaWFibGUg4oCcZGVwcmVzc2lvbiBzaXggbW9udGhzIGFmdGVyIHRoZSBpbnRlcnZlbnRpb24gYXNzaWdubWVudC7igJ0gQXNzdW1lIHRoYXQgWWkoMCkgYW5kIFlpKDEpIGFyZSBpbmRlcGVuZGVudCBhbmQgYXJlIGJvdGggbG9nLW5vcm1hbGx5IGRpc3RyaWJ1dGVkIGNvbmRpdGlvbmFsIG9uIGNvdmFyaWF0ZXMuCgpEZXJpdmUgdGhlIHBvc3RlcmlvciBkaXN0cmlidXRpb25zIG9mIHRoZSBmaW5pdGUgc2FtcGxlIGF2ZXJhZ2UgY2F1c2FsIGVmZmVjdCBhbmQgdGhlIHN1cGVyLXBvcHVsYXRpb24gYXZlcmFnZSBjYXVzYWwgZWZmZWN0LiBQbG90IHRoZSByZXN1bHRpbmcgcG9zdGVyaW9yIGRpc3RyaWJ1dGlvbnMgaW4gYSBoaXN0b2dyYW0gYW5kIHJlcG9ydCB0aGUgZm9sbG93aW5nIHN1bW1hcnkgc3RhdGlzdGljcyBvZiB0aGUgcmVzdWx0aW5nIHBvc3RlcmlvciBkaXN0cmlidXRpb25zOiBtZWFuLCBzdGFuZGFyZCBkZXZpYXRpb24sIG1lZGlhbiwgMi41JSBhbmQgOTcuNSUgcGVyY2VudGlsZXMuIAoKQ29tcGFyZSB0aGUgcmVzdWx0cyB3aXRoIHRob3NlIG9idGFpbmVkIHdpdGhvdXQgY29uZGl0aW9uIG9uIHRoZSBjb3ZhcmlhdGVzCgpgYGB7cn0KbWNtYy5tNiA8LSBmdW5jdGlvbihuaXRlciwgbmJ1cm4sIHRoaW49MSwgIAogICAgICAgICAgICAgICAgICAgICBwYXIucHJpb3IsIE91dGNvbWUub2JzLCBXLCBYLAogICAgICAgICAgICAgICAgICAgICBzZWVkPU5VTEwsIHRoZXRhLnN0YXJ0PU5VTEwsIGNyZWQubGV2ZWwgPSAwLjk1KXsKICAKICAgIFlvYnMgPC0gbG9nKE91dGNvbWUub2JzKSAjIGxvZyB0cmFuc2YKICAgIAogICAgTmM8LSBzdW0oMS1XKTsgTnQ8LSBzdW0oVyk7IE48LSBOdCtOYwogICAgWFggPC0gYXMubWF0cml4KGNiaW5kKDEsWCkpCiAgICBueHg8LW5jb2woWFgpCiAgICAKICAgIGRyYXdzPC0gc2VxKChuYnVybisxKSwgbml0ZXIsIGJ5PXRoaW4pCiAgICBuZHJhd3M8LSBsZW5ndGgoZHJhd3MpCiAgICBqIDwtIDAKICAgIAogICAgIyBTdGFydCB2YWx1ZXMKICAgIGxtLncwPC0gc3VtbWFyeShsbShZb2JzW1c9PTBdIH4gWFtXPT0wLF0pKSAgIyBjb25kIG9uIGNvdmFycwogICAgbG0udzE8LSBzdW1tYXJ5KGxtKFlvYnNbVz09MV0gfiBYW1c9PTEsXSkpCiAgICBpZihpcy5udWxsKHRoZXRhLnN0YXJ0KT09VFJVRSl7CiAgICAKICAgIHRoZXRhIDwtIGxpc3QoYmV0YS5jID0gIGFzLm51bWVyaWMobG0udzAkY29lZmZpY2llbnRzWywxXSkgKyBybm9ybShueHgsMCwgMC4xKSwKICAgICAgICAgICAgICAgICAgYmV0YS50ID0gIGFzLm51bWVyaWMobG0udzEkY29lZmZpY2llbnRzWywxXSkgKyBybm9ybShueHgsMCwgMC4xKSwKICAgICAgICAgICAgICAgICAgc2lnbWEyLmMgPSAgYXMubnVtZXJpYyhsbS53MCRzaWdtYV4yKSArIHJub3JtKDEsMCwgMC4wMSksIAogICAgICAgICAgICAgICAgICBzaWdtYTIudCA9IGFzLm51bWVyaWMobG0udzEkc2lnbWFeMikgICsgcm5vcm0oMSwwLCAwLjAxKSkKICAgIAogIH1lbHNlewogICAgdGhldGE8LSB0aGV0YS5zdGFydAogIH0KICAKICBUaGV0YSA8LSBtYXRyaXgoTkEsIG5kcmF3cywgIGxlbmd0aCh1bmxpc3QodGhldGEpKSApCiAgY29sbmFtZXMoVGhldGEpIDwtIG5hbWVzKHVubGlzdCh0aGV0YSkpCiAgCiAgRXN0aW1hbmRzPC0gbWF0cml4KE5BLCBuZHJhd3MsIDIpCiAgY29sbmFtZXMoRXN0aW1hbmRzKTwtIGMoImF0ZS5mcyIsICJhdGUuc3AiKQogIAogIGlmKGlzLm51bGwoc2VlZCk9PUZBTFNFKXsKICAgIHNldC5zZWVkKHNlZWQpfQogIAogIGZvcihsIGluIDE6bml0ZXIpewogICAgCiAgICAjIFVwZGF0ZSBiZXRhLmMKICAgIE9tZWdhLmMub2JzICAgPC0gc29sdmUoc29sdmUocGFyLnByaW9yJE9tZWdhLmMpICsgdChYWFtXPT0wLF0pJSolWFhbVz09MCxdL3RoZXRhJHNpZ21hMi5jKQogICAgbnUuYy5vYnMgICAgICA8LSBPbWVnYS5jLm9icyUqJShzb2x2ZShwYXIucHJpb3IkT21lZ2EuYyklKiVwYXIucHJpb3IkbnUuYyArIHQoWFhbVz09MCxdKSUqJVlvYnNbVz09MF0vdGhldGEkc2lnbWEyLmMpCiAgICB0aGV0YSRiZXRhLmMgIDwtIGFzLm51bWVyaWMocm12bm9ybSgxLCBtZWFuPSBudS5jLm9icywgc2lnbWE9T21lZ2EuYy5vYnMpKQogICAgCiAgICAjIFVwZGF0ZSBiZXRhLnQKICAgIE9tZWdhLnQub2JzICAgPC0gc29sdmUoc29sdmUocGFyLnByaW9yJE9tZWdhLnQpICsgdChYWFtXPT0xLF0pJSolWFhbVz09MSxdL3RoZXRhJHNpZ21hMi50KQogICAgbnUudC5vYnMgICAgICA8LSBPbWVnYS50Lm9icyUqJShzb2x2ZShwYXIucHJpb3IkT21lZ2EudCklKiVwYXIucHJpb3IkbnUudCArIHQoWFhbVz09MSxdKSUqJVlvYnNbVz09MV0vdGhldGEkc2lnbWEyLnQpCiAgICB0aGV0YSRiZXRhLnQgIDwtIGFzLm51bWVyaWMocm12bm9ybSgxLCBtZWFuPSBudS50Lm9icywgc2lnbWE9T21lZ2EudC5vYnMpKQogICAgCiAgICAjIFVwZGF0ZSBzaWdtYTIuYwogICAgYS5jLm9icyAgICAgICAgPC0gTmMgKyBwYXIucHJpb3IkYS5jCiAgICBiMi5jLm9icyAgICAgICA8LSAge3Bhci5wcmlvciRhLmMqcGFyLnByaW9yJGIyLmMgKyBzdW0oe1lvYnNbVz09MF0tWFhbVz09MCxdJSoldGhldGEkYmV0YS5jfV4yKX0vYS5jLm9icwogICAgdGhldGEkc2lnbWEyLmMgPC0gIHthLmMub2JzKmIyLmMub2JzfS9yY2hpc3EoMSwgYS5jLm9icykKICAgIAogICAgIyBVcGRhdGUgc2lnbWEyLnQKICAgIGEudC5vYnMgICAgICAgIDwtIE50ICsgcGFyLnByaW9yJGEudAogICAgYjIudC5vYnMgICAgICAgPC0ge3Bhci5wcmlvciRhLnQqcGFyLnByaW9yJGIyLnQgKyBzdW0oe1lvYnNbVz09MV0tWFhbVz09MSxdJSoldGhldGEkYmV0YS50fV4yKX0vYS50Lm9icwogICAgdGhldGEkc2lnbWEyLnQgPC0gIHthLnQub2JzKmIyLnQub2JzfS9yY2hpc3EoMSwgYS50Lm9icykgICAgICAKICAgIAogICAgcm0oT21lZ2EuYy5vYnMsIG51LmMub2JzLCBPbWVnYS50Lm9icywgbnUudC5vYnMsICBhLmMub2JzLCBiMi5jLm9icywgYS50Lm9icywgYjIudC5vYnMpCiAgICAKICAgIGlmKHN1bShsID09IGRyYXdzKT09MSl7CiAgICAgIGogPC0gaisxCiAgICAgIAogICAgICBUaGV0YVtqLF08LSB1bmxpc3QodGhldGEpCiAgICAgIAogICAgICAjI0ltcHV0YXRlIHRoZSBtaXNzaW5nIHBvdGVudGlhbCBvdXRjb21lcyB1c2luZyBZbWlzIHwgWW9icywgVywgWCwgdGhldGEKICAgICAgWTA8LVkxPC1OVUxMCiAgICAgIAogICAgICBZMFtXPT0wXTwtIE91dGNvbWUub2JzW1c9PTBdICMgbm8gbG9nIHRyYW5zZgogICAgICBZMFtXPT0xXTwtIGV4cChybm9ybShOdCwgWFhbVz09MSxdJSoldGhldGEkYmV0YS5jLCBzcXJ0KHRoZXRhJHNpZ21hMi5jKSkpICMgaW52ZXJzZSB0cmFucwogICAgICAKICAgICAgWTFbVz09MF08LSBleHAocm5vcm0oTmMsIFhYW1c9PTAsXSUqJXRoZXRhJGJldGEudCwgc3FydCh0aGV0YSRzaWdtYTIudCkpKSAjIGludmVyc2UgdHJhbnMKICAgICAgWTFbVz09MV08LSBPdXRjb21lLm9ic1tXPT0xXSAjIG5vIGxvZyB0cmFuc2YKICAgICAgCiAgICAgIEVzdGltYW5kc1tqLCJhdGUuZnMiXSA8LSBtZWFuKFkxKS1tZWFuKFkwKQogICAgICBFc3RpbWFuZHNbaiwiYXRlLnNwIl0gPC0gbWVhbihleHAoWFglKiV0aGV0YSRiZXRhLnQgKyAwLjUqdGhldGEkc2lnbWEyLnQpKSAtIG1lYW4oZXhwKFhYJSoldGhldGEkYmV0YS5jICsgMC41KnRoZXRhJHNpZ21hMi5jKSkKICAgIH0KICB9CiAgCiAgIyBTdW1tYXJ5IHN0YXRpc3RpY3Mgb2YgdGhlIHNpbXVsYXRlZCBwb3N0ZXJpb3IgZGlzdHJpYnV0aW9uIG9mIEFURS5GUyBhbmQgQVRFLlNQCiAgcHJvYnM8LWMoKDEtY3JlZC5sZXZlbCkvMiwxLSgxLWNyZWQubGV2ZWwpLzIpCiAgCiAgZXN0PC1yb3VuZChjYmluZChhcHBseShFc3RpbWFuZHMsMixtZWFuKSwKICAgICAgICAgICAgICAgICAgIGFwcGx5KEVzdGltYW5kcywyLHNkKSwKICAgICAgICAgICAgICAgICAgIGFwcGx5KEVzdGltYW5kcywyLG1lZGlhbiksCiAgICAgICAgICAgICAgICAgICBhcHBseShFc3RpbWFuZHMsMixmdW5jdGlvbih4KSBxdWFudGlsZSh4LHByb2JzWzFdKSksCiAgICAgICAgICAgICAgICAgICBhcHBseShFc3RpbWFuZHMsMixmdW5jdGlvbih4KSBxdWFudGlsZSh4LHByb2JzWzJdKSkpLDQpCiAgCiAgY29sbmFtZXMoZXN0KTwtYygiTWVhbiIsIiBzZCIsIiBNZWRpYW4iLCIgQ0kgbG93IiwiIENJIHVwIikKICBwcmludChlc3QpCiAgCiAgCiAgcGFybXM8LXJvdW5kKGNiaW5kKGFwcGx5KFRoZXRhLDIsbWVhbiksICAKICAgICAgICAgICAgICAgICAgICAgYXBwbHkoVGhldGEsMixzZCkpLDQpCiAgY29sbmFtZXMocGFybXMpPC1jKCJNZWFuIiwiIHNkIikKICBwcmludChwYXJtcykKICAKICByZXR1cm4obGlzdChUaGV0YT1UaGV0YSwgRXN0aW1hbmRzPUVzdGltYW5kcykpCn0KCgpYIDwtIGFzLm1hdHJpeChkZl9qb2JzWywgYygic2V4IiwiYWdlIiwicmFjZSIsIm5vbm1hcnJpZWQiLCJlZHVjIiwiRWNvbkhhcmQiLCJhc3NlcnRpdmUiLCJtb3RpdmF0aW9uIiwiZGVwcmVzczAiKV0pCm5jb3Y8LSBuY29sKFgpCgpwYXIucHJpb3IgPC0gbGlzdChudS5jPXJlcCgwLCB7bmNvdisxfSksIE9tZWdhLmM9ZGlhZygxMDBeMix7bmNvdisxfSksIAogICAgICAgICAgICAgICAgICBudS50PXJlcCgwLCB7bmNvdisxfSksIE9tZWdhLnQ9ZGlhZygxMDBeMix7bmNvdisxfSksCiAgICAgICAgICAgICAgICAgIGEuYz0yLCBiMi5jPTAuMDEsIGEudD0yLCBiMi50PTAuMDEpCgpjaGFpbi5tQzwtbWNtYy5tNihuaXRlcj0yMDAwMCwgbmJ1cm49NTAwMCwgdGhpbj0xLCAgCiAgICAgICAgICAgICAgICAgIHBhci5wcmlvciwgCiAgICAgICAgICAgICAgICAgIE91dGNvbWUub2JzPVlvYnMsIFc9WiwgWD1YLCAKICAgICAgICAgICAgICAgICAgc2VlZD0yMDIyLCB0aGV0YS5zdGFydD1OVUxMKQoKIyMgT3ZlcmxhcHBpbmcgaGlzdG9ncmFtcyBvZiB0aGUgc2ltdWxhdGVkIHBvc3RlcmlvciBkaXN0cmlidXRpb24gb2YgQVRFLkZTCmhpc3QoY2hhaW4ubUMkRXN0aW1hbmRzWywiYXRlLmZzIl0sIGZyZXE9RkFMU0UsIGJyZWFrcyA9IDIwLAogICAgIG1haW4gPSAiQXZlcmFnZSBUcmVhdG1lbnQgRWZmZWN0IiwgCiAgICAgeGxhYj0iIix5bGFiPSIiLCAKICAgICBjZXgubGFiPTIuNSwgZGVuc2l0eT0zMCwgY29sPSIjRkM0RTA3IikKCmhpc3QoY2hhaW4ubUMkRXN0aW1hbmRzWywiYXRlLnNwIl0sIGZyZXE9RkFMU0UsIGJyZWFrcz0yMCxhZGQ9VFJVRSwgY29sPSIjMDBBRkJCIiwgZGVuc2l0eT0yMCkKCmxlZ2VuZCgidG9wcmlnaHQiLCBjZXg9YygwLjgsMC44LDAuOCksIGx0eT1jKDEsMSwxKSwgY29sPSBjKCIjRkM0RTA3IiwgIiMwMEFGQkIiKSwgbGVnZW5kPWMoIkFURS5GUyIsIkFURS5TUCIpKQoKYGBgCgpgYGB7cn0KIyMgT3ZlcmxhcHBpbmcgaGlzdG9ncmFtcyBvZiB0aGUgc2ltdWxhdGVkIHBvc3RlcmlvciBkaXN0cmlidXRpb24gb2YgQVRFLkZTCmhpc3QoY2hhaW4ubUIkRXN0aW1hbmRzWywiYXRlLmZzIl0sIGZyZXE9RkFMU0UsIGJyZWFrcyA9IDIwLAogICAgIG1haW4gPSAiRmluaXRlLVNhbXBsZSBBdmVyYWdlIFRyZWF0bWVudCBFZmZlY3QiLCAKICAgICB4bGFiPSIiLHlsYWI9IiIsIAogICAgIGNleC5sYWI9Mi41LCBkZW5zaXR5PTMwLCBjb2w9IiNGQzRFMDciKSAgIyAiI0ZDNEUwNyIsICIjMDBBRkJCIgoKaGlzdChjaGFpbi5tQyRFc3RpbWFuZHNbLCJhdGUuZnMiXSwgZnJlcT1GQUxTRSwgYnJlYWtzPTIwLGFkZD1UUlVFLCBjb2w9IiMwMEFGQkIiLCBkZW5zaXR5PTIwKQpsZWdlbmQoInRvcHJpZ2h0IiwgY2V4PWMoMC44LDAuOCwwLjgpLCBsdHk9YygxLDEsMSksIGNvbD0gYygiI0ZDNEUwNyIsICIjMDBBRkJCIiksIGxlZ2VuZD1jKCJNb2RlbCBCIiwiTW9kZWwgQyIpKSAKCiMjIE92ZXJsYXBwaW5nIGhpc3RvZ3JhbXMgb2YgdGhlIHNpbXVsYXRlZCBwb3N0ZXJpb3IgZGlzdHJpYnV0aW9uIG9mIEFURS5QUwpoaXN0KGNoYWluLm1CJEVzdGltYW5kc1ssImF0ZS5zcCJdLCBmcmVxPUZBTFNFLCBicmVha3MgPSAyMCwKICAgICBtYWluID0gIlN1cGVyLVBvcHVsYXRpb24gQXZlcmFnZSBUcmVhdG1lbnQgRWZmZWN0IiwgCiAgICAgeGxhYj0iIix5bGFiPSIiLCAKICAgICBjZXgubGFiPTIuNSwgZGVuc2l0eT0zMCwgY29sPSIjRkM0RTA3IikgICMgIiNGQzRFMDciLCAiIzAwQUZCQiIKCmhpc3QoY2hhaW4ubUMkRXN0aW1hbmRzWywiYXRlLnNwIl0sIGZyZXE9RkFMU0UsIGJyZWFrcz0yMCxhZGQ9VFJVRSwgY29sPSIjMDBBRkJCIiwgZGVuc2l0eT0yMCkKbGVnZW5kKCJ0b3ByaWdodCIsIGNleD1jKDAuOCwwLjgsMC44KSwgbHR5PWMoMSwxLDEpLCBjb2w9IGMoIiNGQzRFMDciLCAiIzAwQUZCQiIpLCBsZWdlbmQ9YygiTW9kZWwgQiIsIk1vZGVsIEMiKSkKYGBgCkxvb2tpbmcgYXQgdGhlIHRoYXQgZGlzdHJpYnV0aW9ucywgZ29pbmcgZnJvbSB0aGUgbW9kZWwgQiB3aXRob3V0IGNvbmRpdGlvbiBvbiBjb3ZhcmlhdGVzIHRvIG1vZGVsIEMgd2l0aCBpdCwgdGhpcyBzZWVtIHRvIGhhdmUgYW4gZWZmZWN0IG9uIHRoZSBvdXRjb21lLCBjb25zaWRlcmluZywgZS5nLiB0aGF0IHRoZSBjdXJ2ZSBhcmUgc2hpZnRlZCB0byB0aGUgbGVmdC4KCgojIFBBUlQgMiB8IE9ic2VydmF0aW9uYWwgU3R1ZGllcwojIyAxLiBMb2FkIHRoZSBkYXRhc2V0LiBUZW1wb3JhcmlseSByZW1vdmUgdGhlIG91dGNvbWUsIE9VVENPTUUsIGZyb20gdGhlIGRhdGEgc2V0LgoKYGBge3J9CmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShNYXRjaEl0KQpgYGAKCgpgYGB7cn0KIyBpbXBvcnQgZmlsZQpkZl9sdXhfYWxsIDwtIHJlYWQudGFibGUoJ1RyYWluaW5nTHV4LnR4dCcpCmRmX2x1eF9hbGwKYGBgCgoKCmBgYHtyfQojIGRmIHdpdGhvdSBPdXRjb21lCmRmX2x1eCA8LSBkZl9sdXhfYWxsWywgIShuYW1lcyhkZl9sdXhfYWxsKSA9PSAiT3V0Y29tZSIpXQpkZl9sdXgKCiMgQ292YXJpYXRlcwpYIDwtIGRmX2x1eFssICEobmFtZXMoZGZfbHV4KSA9PSAiVFJFQVQiKV0KWApgYGAKCgojIyAyLiBGb3IgZWFjaCBjb3ZhcmlhdGUsIGRpc3BsYXkgdGhlIG1lYW4gd2l0aGluIGVhY2ggdHJlYXRtZW50IGdyb3VwIGFuZCB0aGUgc3RhbmRhcmRpemVkIGRpZmZlcmVuY2UgaW4gYSB0YWJsZS4KCmBgYHtyfQpzdGRfZGlmZiA8LSBmdW5jdGlvbih4LCB0cmVhdCl7CiAgCiAgIyMjIFBhcmFtIGNoZWNrcwogICMgQ2hlY2sgZm9yIE5BcyBpbiB4CiAgaWYoYW55KGlzLm5hKHgpKSl7d2FybmluZygiTkFzIHJlbW92ZWQsIGNoZWNrIHggYW5kIHRoaW5rIG9mIG90aGVyIG9wdGlvbnMiKX0KICAKICAjIENoZWNrIGZvciBOQXMgaW4gdHJlYXQKICBpZihhbnkoaXMubmEodHJlYXQpKSl7c3RvcCgidHJlYXRtZW50IGluZGljYXRvciAndHJlYXQnIGlzIG5vdCBzdXBwb3NlZCB0byBjb250YWluIE5BLCBjaGVjayBpdCEiKX0KICAKICAjIE1lYW4KICBtZWFuIDwtIG1lYW4oeCwgbmEucm0gPSBUUlVFKQogIAogICMjIyBNZWFuIG9mICd4JyBhbW9uZyB0cmVhdGVkIGFuZCBjb250cm9sIHVuaXRzCiAgbWVhbi50IDwtIG1lYW4oeFt0cmVhdCA9PSAxXSwgbmEucm0gPSBUUlVFKQogIG1lYW4uYyA8LSBtZWFuKHhbdHJlYXQgPT0gMF0sIG5hLnJtID0gVFJVRSkKICAKICAjIyMgVmFyaWFuY2Ugb2YgJ3gnIGFtb25nIHRyZWF0ZWQgYW5kIGNvbnRyb2wgdW5pdHMKICB2YXIudCA8LSB2YXIoeFt0cmVhdCA9PSAxXSwgbmEucm0gPSBUUlVFKQogIHZhci5jIDwtIHZhcih4W3RyZWF0ID09IDBdLCBuYS5ybSA9IFRSVUUpCiAgCiAgIyMjIFN0YW5kYXJkaXplZCBkaWZmZXJlbmNlIGluIG1lYW5zCiAgc3RkLmRpZmYgPC0gKG1lYW4udCAtIG1lYW4uYykgLyBzcXJ0KCh2YXIudCArIHZhci5jKS8yKQogIAogICMjIyBSZXR1cm5pbmcgcmVzdWx0cwogIHJlcyA8LSBjKE1lYW4gPSBtZWFuLCBNZWFuLnQgPSBtZWFuLnQsIE1lYW4uYyA9IG1lYW4uYywgU2QudCA9IHNxcnQodmFyLnQpLCBTZC5jID0gc3FydCh2YXIuYyksIFN0ZC5NZWFuLkRpZmYgPSBzdGQuZGlmZikKICByZXR1cm4ocmVzKQp9Cgp0KHNhcHBseShYLCBzdGRfZGlmZiwgdHJlYXQgPSBkZl9sdXgkVFJFQVQsIHNpbXBsaWZ5ID0gVFJVRSkpCmBgYAoKCiMjIDMuIEVzdGltYXRlIGEgcHJvcGVuc2l0eSBzY29yZSBmb3IgZWFjaCB1bml0IGluIHRoZSBvYnNlcnZhdGlvbmFsIHN0dWR5IHVzaW5nIHRoZSBmaXR0ZWQgdmFsdWVzIGZyb20gYSBsb2dpc3RpYyByZWdyZXNzaW9uIHdpdGggbWFpbiBlZmZlY3RzIGZvciBhbGwgb2YgdGhlIGNvdmFyaWF0ZXMgY29udGFpbmVkIGluIHRoZSBkYXRhIHNldCAobGVhdmUgb3V0IHRyYW5zZm9ybWF0aW9ucyBhbmQgaW50ZXJhY3Rpb25zIGhlcmUsIGJ1dCB3ZSB3b3VsZCBnZW5lcmFsbHkgd2FudCB0byBleHBsb3JlIHRoZXNlIG90aGVyIHRlcm1zKS4KCkNvbXBhcmUgZ3JhcGhpY2FsbHkgdGhlIGRpc3RyaWJ1dGlvbnMgb2YgZXN0aW1hdGVkIHByb3BlbnNpdHkgc2NvcmVzIHdpdGhpbiB0aGUgdHJlYXRtZW50IGdyb3VwcyBhbmQgZXhwbGFpbiB3aGF0IHlvdSBzZWUuCgpgYGB7cn0KIyBwcm9wZW5zaXR5IHNjb3JlCm1vZCA8LSBnbG0oVFJFQVR+LiwgZGF0YT1kZl9sdXgsIGZhbWlseT1iaW5vbWlhbChsaW5rPWxvZ2l0KSkKc3VtbWFyeShtb2QpCnBzY29yZXMgPC0gbW9kJGZpdHRlZC52YWx1ZXMKCnN0ZF9kaWZmKHBzY29yZXMsIHRyZWF0ID0gZGZfbHV4JFRSRUFUKQoKIyBkZW5zIG92ZXJsYXAKZGVuc2l0eV9vdmVybGFwIDwtIGZ1bmN0aW9uKHgsIHRyZWF0LCBhbHBoYSA9IDAuMjUpewogIAogICMjIyBGb3JtYXR0aW5nIGRhdGEKICBkYXRhIDwtIGRhdGEuZnJhbWUoTGVnZW5kID0gYyhyZXAoIlRyZWF0ZWQiLCBzdW0odHJlYXQpKSwgcmVwKCJDb250cm9scyIsIHN1bSh0cmVhdCA9PSAwKSkpLCBWYWx1ZSA9IGMoeFt0cmVhdCA9PTFdLCB4W3RyZWF0ID09IDBdKSkKICAKICAjIyMgQ2FsbGluZyAnZ2dwbG90JwogIGdncGxvdChkYXRhLCBhZXMoeCA9IFZhbHVlLCBmaWxsID0gTGVnZW5kKSkgKyBnZW9tX2RlbnNpdHkoYWxwaGEgPSBhbHBoYSkKfQoKCiMgSGlzdCBvdmVybGFwCmhpc3Rfb3ZlcmxhcCA8LSBmdW5jdGlvbih4LCB0cmVhdCwgYWxwaGEgPSAwLjUsIC4uLil7CiAgCiAgIyMjIEZvcm1hdHRpbmcgZGF0YQogIGRhdGEgPC0gZGF0YS5mcmFtZShMZWdlbmQgPSBjKHJlcCgiVHJlYXRlZCIsIHN1bSh0cmVhdCkpLCByZXAoIkNvbnRyb2xzIiwgc3VtKHRyZWF0ID09IDApKSksIFZhbHVlID0gYyh4W3RyZWF0ID09MV0sIHhbdHJlYXQgPT0gMF0pKQogIAogICMjIyBDYWxsaW5nICdnZ3Bsb3QnCiAgZ2dwbG90KGRhdGEsIGFlcyh4ID0gVmFsdWUsIGZpbGwgPSBMZWdlbmQsIGFmdGVyX3N0YXQoZGVuc2l0eSkpKSArIGdlb21faGlzdG9ncmFtKGFscGhhID0gYWxwaGEsIHBvc2l0aW9uID0gImlkZW50aXR5IiwgLi4uKQp9CgpkZW5zaXR5X292ZXJsYXAoeCA9IHBzY29yZXMsIHRyZWF0ID0gZGZfbHV4JFRSRUFUKQpoaXN0X292ZXJsYXAoeCA9IHBzY29yZXMsIHRyZWF0ID0gZGZfbHV4JFRSRUFULCBiaW5zID0gMjApCmBgYApUaGUgbm9uLXRyZWF0ZWQgZGlzdHJpYnV0aW9uIGlzIG1vcmUgY29uZGVuc2VkIHRvIHRoZSBvcmlnaW4sIHNob3dpbmcgYSBzaWduaWZpY2FudCBkaWZmZXJlbmNlIG9uIGRpc3RyaWJ1dGlvbnMgb2YgdGhlIHRyZWF0ZWQgYW5kIGNvbnRyb2wgZ3JvdXBzLgoKV2UgYWxyZWFkeSBrbm93IHRoYXQgaW4gdGhpcyBvYnNlcnZhdGlvbmFsIHN0dWR5LCBzbyBpbiB0aGlzIGRhdGFzZXQsIHN1YmplY3RzIHdlcmUgbm90IHJhbmRvbWx5IGFzc2lnbmVkIHRvIHRyZWF0bWVudCBhbmQgdGhpcyBncmFwaCB1bmRlcmxpbmUgdGhpcyBzdGF0bWVudC4gVGhlIHRyZWF0bWVudCBhc3NpZ25tZW50IGhhdmUgYmVlbiBhZmZlY3RlZCBieSBzcGVjaWZpYyB2YWx1ZXMgb2YgdW5pdHMnIGNvdmFyaWF0ZXMsIGluIGEgcHJlZmVyZW50aWFsIHdheSBjb25zaWRlcmluZyBzb21lIGNoYXJhY3RlcmlzdGljcywgc2hvd2luZyBhIGxhY2sgb2YgcmFuZG9tbmVzcy4KCgoKIyMgNC4gV2hhdCBhcmUgdGhlIGltcGxpY2F0aW9ucyBvZiB0aGUgZmFjdCB0aGF0IHRoZSBwcm9wZW5zaXR5IHNjb3JlIGlzIGEgYmFsYW5jaW5nIHNjb3JlPwoKUHJvcGVuc2l0eSBzY29yZSBpcyB1c2VmdWwgZm9yIGFjaGlldmluZyBjb3ZhcmlhdGUgYmFsYW5jZSBpbiBvYnNlcnZhdGlvbmFsIHN0dWRpZXMsIHNpbmNlIHRoZSBsYWNrIG9mIGJhbGFuY2UgY291bGQgaW5mbHVlbmNlIHRoZSByZWxpYWJpbGl0eSBvZiBjYXVzYWwgZWZmZWN0IGVzdGltYXRlcywgYXMgdGhlIHRyZWF0ZWQgYW5kIGNvbnRyb2wgZ3JvdXBzIG1heSBub3QgYmUgY29tcGFyYWJsZSB3aXRoIHJlc3BlY3QgdG8gb2JzZXJ2ZWQgY292YXJpYXRlcy4gCgpUaGUgcHJvcGVuc2l0eSBzY29yZSBzZXJ2ZXMgYXMgYSBzdW1tYXJ5IG1lYXN1cmUgb2YgdGhlIGNvdmFyaWF0ZXMsIGFuZCBob3cgdGhlIGRpc3RyaWJ1dGlvbiBvZiBYIGFwcGVhcnMgYmFsYW5jZWQgYmV0d2VlbiB0cmVhdGVkIGFuZCBjb250cm9sIGdyb3Vwcy4KClRoZSBwcm9wZW5zaXR5IHNjb3JlIGlzIHRoZSBjb2Fyc2VzdCBiYWxhbmNpbmcgc2NvcmUsIGl0IGlzIGEgZnVuY3Rpb24gb2YgZXZlcnkgYmFsYW5jaW5nIHNjb3JlLCBzbyBpdCBwcm92aWRlcyB0aGUgYmlnZ2VzdCBiZW5lZml0IGluIHRlcm1zIG9mIHJlZHVjaW5nIHRoZSBudW1iZXIgb2YgdmFyaWFibGVzIHdlIG5lZWQgdG8gYWRqdXN0IGZvci4KCkl0cyBhZHZhbnRhZ2UgbGllcyBpbiBzaWduaWZpY2FudGx5IHJlZHVjaW5nIHRoZSBudW1iZXIgb2YgdmFyaWFibGVzIHJlcXVpcmluZyBhZGp1c3RtZW50LgoKCgojIyA1LiBUcmltbWluZyBhbmQgc3ViY2xhc3NpZmljYXRpb24gb24gcHJvcGVuc2l0eSBzY29yZS4gTm93IGFzc2VzcyBiYWxhbmNlIGFuZCBjcmVhdGUgYmFsYW5jZWQgZ3JvdXBzLgoKIyMjIChhKSBJbiBvcmRlciB0byBkbyBzbywgZmlyc3QgZGlzY2FyZCBjb250cm9sIHVuaXRzIHdpdGggZXN0aW1hdGVkIHByb3BlbnNpdHkgc2NvcmVzIGxvd2VyIHRoYW4gdGhlIG1pbmltdW0gb2YgdGhlIGFjdGl2ZSB0cmVhdGVkIHVuaXRzIGVzdGltYXRlZCBwcm9wZW5zaXR5IHNjb3JlcyBvciBoaWdoZXIgdGhhbiB0aGUgbWF4aW11bSBvZiB0aGUgYWN0aXZlIHRyZWF0bWVudCB1bml0cyBlc3RpbWF0ZWQgcHJvcGVuc2l0eSBzY29yZXMuIAoKSG93IG1hbnkgdW5pdHMgZGlkIHlvdSBkaXNjYXJkPyBXaHkgaXMgaXQgaW1wb3J0YW50IHRvIGRpc2NhcmQgdGhlc2UgdW5pdHM/CgpgYGB7cn0KIyMjIDIpIFRSSU1NSU5HCgojIFdlIHdhbnQgdG8gZGlzY2FyZCBjb250cm9sIHVuaXRzIGhhdmluZyBQUyBsb3dlciB0aGFuIHRoZSBtaW4gUFMgZm9yIHRoZSB0cmVhdGVkLiAKIyBXZSBjb3VsZCBhbHNvIGRpc2NhcmQgdHJlYXRlZCB1bml0cyBmb3IgYSBjb21tb24gc3VwcG9ydCBidXQgaW4gdGhpcyBjYXNlIHdlIHByZWZlciB0byBzYWx2YWdlIGFsbCB0aGUgbiB0cmVhdGVkCgpieShwc2NvcmVzLGRmX2x1eCRUUkVBVCxzdW1tYXJ5KSAKCm1pbmN1dCA8LSBtaW4ocHNjb3Jlc1tkZl9sdXgkVFJFQVQ9PTFdKQptYXhjdXQgPC0gbWF4KHBzY29yZXNbZGZfbHV4JFRSRUFUPT0xXSkKCmRmX2x1eDIgPC0gZGZfbHV4W3BzY29yZXM+PW1pbmN1dCAmIHBzY29yZXM8PW1heGN1dCxdCgpucm93KGRmX2x1eCkgLSBucm93KGRmX2x1eDIpICAjIG51bWJlciBkaXNjYXJkZWQKdGFibGUoZGZfbHV4MiRUUkVBVCkgICAgICAgICAgIyBvbmx5IGNvbnRyb2xzIGRpc2NhcmRlZCwgYXMgd2Ugd2FudGVkCmRpc2NhcmRlZCA8LSB3aGljaChwc2NvcmVzIDwgbWluY3V0IHwgcHNjb3JlcyA+IG1heGN1dCkgIyBkaXNjYXJkZWQgdW5pdHMKI2Rpc2NhcmRlZApgYGAKSSd2ZSBkaXNjYXJkZWQgMzcwIHVuaXRzLiAKSXQncyBpbXBvcnRhbnQgdG8gZGlzY2FyZCB0aGVzZSB1bml0cyB0byBhY2hpZXZlIGNvbW1vbiBzdXBwb3J0IGJldHdlZW4gdGhlIHRyZWF0bWVudCBhbmQgY29udHJvbCBncm91cHMsIGVuc3VyaW5nIHRoYXQgdGhlIGRpc3RyaWJ1dGlvbiBvZiBlc3RpbWF0ZWQgcHJvcGVuc2l0eSBzY29yZXMgb3ZlcmxhcHMgZm9yIGJvdGggZ3JvdXBzLiBUaGlzIGhlbHBzIGluIGNyZWF0aW5nIGJhbGFuY2VkIGdyb3VwcyBhbmQgaW1wcm92ZSB0aGUgbGFjayBvZiByYW5kb21pemF0aW9uLiBSZW1vdmluZyB1bml0cyBtYWtlIHRoZSBlc3RpbWF0ZWQgdHJlYXRtZW50IGVmZmVjdHMgbW9yZSByZWxpYWJsZSBhbmQgcmVkdWNlIHRoZSBwb3RlbnRpYWwgZm9yIGJpYXMuCgoKYGBge3J9CiMgMi4xKSBTdGFuZGFyZGl6ZWQgRGlmZi4gaW4gTWVhbiBmb3IgdGhlIHRyaW1tZWQgZGF0YQpYLnRyaW0gPC0gZGZfbHV4MlssICEobmFtZXMoZGZfbHV4MikgPT0gIlRSRUFUIildCnQoc2FwcGx5KFgudHJpbSwgc3RkX2RpZmYsIHRyZWF0ID0gZGZfbHV4MiRUUkVBVCwgc2ltcGxpZnkgPSBUUlVFKSkKCiMgMi4yKSBSZS1lc3RpbWF0aW9uIG9mIHRoZSBwcm9wZW5zaXR5IHNjb3JlCm1vZDIgPC0gZ2xtKFRSRUFUfi4sIGRhdGEgPSBkZl9sdXgyLCBmYW1pbHkgPSBiaW5vbWlhbChsaW5rPWxvZ2l0KSkKc3VtbWFyeShtb2QyKQpwc2NvcmVzMiA8LSBtb2QyJGZpdHRlZC52YWx1ZXMKCiMgMi4zKSBCYWxhbmNlIGFzc2Vzc21lbnQgKFN0YW5kYXJkaXplZCBEaWZmLiBpbiBNZWFuIGFuZCBncmFwaGljYWwgY2hlY2tzKQpzdGRfZGlmZihwc2NvcmVzMiwgdHJlYXQgPSBkZl9sdXgyJFRSRUFUKQpoaXN0X292ZXJsYXAocHNjb3JlczIsIHRyZWF0ID0gZGZfbHV4MiRUUkVBVCkKYGBgCgoKIyMjIChiKSBVc2luZyB0aGUgdW5pdHMgcmVtYWluaW5nIGFmdGVyIDUoYSksIGNyZWF0ZSBmaXZlIHN1YmNsYXNzZXMgYmFzZWQgb24gdGhlIGVzdGltYXRlZCBwcm9wZW5zaXR5IHNjb3JlLiAKWW91IGFyZSBhbGxvd2VkIHRvIGNob29zZSBzaXplIGFuZCBib3VuZHMgb2YgdGhlIHN1YmNsYXNzZXMuIFlvdSBhcmUgc3VwcG9zZWQgdG8gY3JlYXRlIHRoZSBiZXN0IHN1YmNsYXNzZXMgYmFzZWQgb24geW91ciBvd24gcmVhc29uaW5nLiBDcmVhdGUgYSB0YWJsZSBzaG93aW5nIHRoZSBudW1iZXIgb2YgdHJlYXRlZCBhbmQgY29udHJvbCB1bml0cyB3aXRoaW4gZWFjaCBvZiB0aGUgZml2ZSBzdWJjbGFzc2VzLgoKQnJpZWZseSBjb21tZW50IGFuZCBleHBsYWluIHlvdXIgY2hvaWNlLiAoSGludDogbG9vayBhdCB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZSBwcm9wZW5zaXR5IHNjb3JlIGluIHRoZSB0d28gZ3JvdXBzLCBjaGVjayB0aGUgb3ZlcmxhcCwgY2hlY2sgdGhlIGJhbGFuY2Ugd2l0aGluIHN1YmNsYXNzZXMgYW5kIHRoZSBudW1iZXIgb2YgdHJlYXRlZCBhbmQgY29udHJvbCB1bml0cywgZXRjKS4KCmBgYHtyfQojIyMgMykgU1VCQ0xBU1NJRklDQVRJT04gQkFTRUQgT04gVEhFIFBST1BFTlNJVFkgU0NPUkUKCiMgVGhlIGdvYWwgaXMgdG8gY3JlYXRlIHN1YmNsYXNzZXMgb2YgdHJlYXRlZCBhbmQgY29udHJvbCB1bml0cyBzaGFyaW5nCiMgc2ltaWxhciB2YWx1ZXMgZm9yIHRoZSBwcm9wZW5zaXR5IHNjb3JlCgojIDMuMSkgRGVmaW5pbmcgc3ViY2xhc3NlcyBmcm9tIHRoZSBpbnNwZWN0aW9uIG9mIHRoZSBxdWFudGlsZXMgdGFibGUKcXVhbnQudGFiIDwtIGRhdGEuZnJhbWUoUXVhbnRpbGVzID0gc2VxKDAsMSwgYnk9MC4wNSksCiAgICAgICAgICAgICAgICAgICAgICAgIFBTLkNvbnRyb2xzID0gYXMubnVtZXJpYyhxdWFudGlsZShwc2NvcmVzMltkZl9sdXgyJFRSRUFUPT0wXSxwcm9icz1zZXEoMCwxLCBieT0wLjA1KSkpLAogICAgICAgICAgICAgICAgICAgICAgICBQUy5UcmVhdGVkID0gYXMubnVtZXJpYyhxdWFudGlsZShwc2NvcmVzMltkZl9sdXgyJFRSRUFUPT0xXSxwcm9icz1zZXEoMCwxLCBieT0wLjA1KSkpLAogICAgICAgICAgICAgICAgICAgICAgICBQUy5XaG9sZSA9IGFzLm51bWVyaWMocXVhbnRpbGUocHNjb3JlczIscHJvYnM9c2VxKDAsMSwgYnk9MC4wNSkpKSkKCmJyZWFrcyA8LSBxdWFudGlsZShwc2NvcmVzMiwgYyguNjUsIC44MCwgLjg1LCAuOSkpICAjLCAuOTUpKSAgIyAuNCwgLjc1LCAuODUsIC45LCAuOTUpKSAKYmlucyA8LSByZXAoTkEsbnJvdyhkZl9sdXgyKSkKYmluc1twc2NvcmVzMjw9YnJlYWtzWzFdXSA8LSAxCmJpbnNbcHNjb3JlczI+YnJlYWtzWzFdICYgcHNjb3JlczI8PWJyZWFrc1syXV0gPC0gMgpiaW5zW3BzY29yZXMyPmJyZWFrc1syXSAmIHBzY29yZXMyPD1icmVha3NbM11dIDwtIDMKYmluc1twc2NvcmVzMj5icmVha3NbM10gJiBwc2NvcmVzMjw9YnJlYWtzWzRdXSA8LSA0CmJpbnNbcHNjb3JlczI+YnJlYWtzWzRdXSA8LTUgIyAmIHBzY29yZXMyPD1icmVha3NbNV1dIDwtIDUKI2JpbnNbcHNjb3JlczI+YnJlYWtzWzVdXSA8LSA2Cgp0YWJsZShiaW5zLCBkZl9sdXgyJFRSRUFUKSAgICAgICAgIyBOdW1iZXIgb2YgVCBhbmQgQyBpbiBlYWNoIHN1YmNsYXNzCnRhYmxlKGJpbnMpICAgICAgICAgICAgICAgICAgICAjIFRvdCBudW1iZXIgb2YgcGVvcGxlIGluIGVhY2ggc3ViY2xhc3MKCiMgMy4yKSBCYWxhbmNlIGFzc2Vzc21lbnQgd2l0aGluIGVhY2ggYmxvY2sKIyBTdGFuZGFyZGl6ZWQgRGlmZi4gaW4gTWVhbgptYXBwbHkoMTpsZW5ndGgodW5pcXVlKGJpbnMpKSwgRlVOID0gZnVuY3Rpb24oYikoc3RkX2RpZmYocHNjb3JlczJbYmlucyA9PSBiXSwgdHJlYXQgPSBkZl9sdXgyJFRSRUFUW2JpbnMgPT0gYl0pKSkKIyBtYXBwbHkoWC50cmltLCBGVU4gPSBmdW5jdGlvbih4KShzdGRfZGlmZl9ibG9jayh4LCB0cmVhdCA9IGRmX2x1eDIkVFJFQVQsIGJsb2NrcyA9IGJpbnMsIHdlaWdodHMgPSAidG90YWwiKSkpCmBgYAoKCgooYykgVXNlIGRlc2NyaXB0aXZlIHRvb2xzIChncmFwaHMgYW5kIHN0YXRpc3RpY3MpIHRvIGFzc2VzcyBjb3ZhcmlhdGUgYmFsYW5jZS4KCmBgYHtyfQojIEdyYXBoaWNhbCBiYWxhbmNlIGFzc2Vzc21lbnQKZGVuc2l0eV9vdmVybGFwIDwtIGZ1bmN0aW9uKHgsIHRyZWF0LCBhbHBoYSA9IDAuMjUpewogIAogICMjIyBGb3JtYXR0aW5nIGRhdGEKICBkYXRhIDwtIGRhdGEuZnJhbWUoTGVnZW5kID0gYyhyZXAoIlRyZWF0ZWQiLCBzdW0odHJlYXQpKSwgcmVwKCJDb250cm9scyIsIHN1bSh0cmVhdCA9PSAwKSkpLCBWYWx1ZSA9IGMoeFt0cmVhdCA9PTFdLCB4W3RyZWF0ID09IDBdKSkKICAKICAjIyMgQ2FsbGluZyAnZ2dwbG90JwogIGdncGxvdChkYXRhLCBhZXMoeCA9IFZhbHVlLCBmaWxsID0gTGVnZW5kKSkgKyBnZW9tX2RlbnNpdHkoYWxwaGEgPSBhbHBoYSkKfQoKbWFwcGx5KHVuaXF1ZShiaW5zKVtvcmRlcih1bmlxdWUoYmlucykpXSwgCiAgICAgICBGVU4gPSBmdW5jdGlvbihCKShkZW5zaXR5X292ZXJsYXAoeCA9IHBzY29yZXMyW2JpbnMgPT0gQl0sIHRyZWF0ID0gZGZfbHV4MiRUUkVBVFtiaW5zID09IEJdKSArCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGdndGl0bGUocGFzdGUoIk92ZXJsYXAsIGJsb2NrIiwgQikpKSwgU0lNUExJRlkgPSBGQUxTRSkKYGBgCkFmdGVyIHNvbWUgYXR0ZW1wcyBJIHJlYWNoZWQgdGhpcyBhcyBteSBiZXN0IGZpdHRpbmcgb3ZlcmxhcCBiZXR3ZWVuIFRyYXRlZCBhbmQgQ29udHJvbC4KVGhlIGZpcnN0IGJsb2NrIGhhcyBhIHNpZ25pZmljYW50IGFyZWEgb2Ygbm9uLW92ZXJsYXBwaW5nLCBidXQgdGhlIG90aGVyIGJsb2NrcyBzaG93IGEgYmV0dGVyIHNpdHVhdGlvbi4KVGhhdCdzIHVuZGVybGluZWQgYWxzbyBpbiB0aGUgc3RhdCB0YWJsZSB3aGVyZTogTWVhbi50ID0gMC4wMTQ2MjgwNjEsIE1lYW4uYyA9IDAuMDA4ODc5OTQ3IGluIGNhc2Ugb2YgYmxvY2sgMSwgc2hvd2luZyBhIHNpZ25pZmljYW50IGRpZmZlcmVuY2UuIFNpbWlsYXJseSwgaW4gYmxvY2sgNSwgYnV0IHN0aWxsIHdpdGggYSBnb29kIGZpdHRpbmcuCgoKIyMgNi4gQW5hbHlzaXMgcGhhc2UKCihhKSBOb3cgdGhhdCB0aGUgc3R1ZHkgZGVzaWduIHBoYXNlIGlzIGNvbXBsZXRlLCByZWFkIGluIHRoZSBvdXRjb21lLCBPdXRjb21lLgoKKGIpIE5haXZlbHkgcHJldGVuZGluZyB0aGF0IHRoaXMgb2JzZXJ2YXRpb25hbCBzdHVkeSB3YXMgYWN0dWFsbHkgYSBjb21wbGV0ZWx5IHJhbmRvbWl6ZWQgZXhwZXJpbWVudCwgYW5kIGlnbm9yaW5nIGNvdmFyaWF0ZXMsIGNhbGN1bGF0ZSBhIE5leW1hbiBlc3RpbWF0ZSBvZiB0aGUgYXZlcmFnZSB0cmVhdG1lbnQgZWZmZWN0IGFuZCBhIGxhcmdlIHNhbXBsZSA5NSUgaW50ZXJ2YWwgYW5kIGVudGVyIGJvdGggaW4gYSB0YWJsZS4KCihjKSBDYWxjdWxhdGUgdGhlIG5haXZlIE5leW1hbiBlc3RpbWF0ZSBhcyBpbiB0aGUgcHJldmlvdXMgcG9pbnQsIGJ1dCBvbiB0aGUgc3Vic2V0IG9mIHVuaXRzIG9idGFpbmVkIGluIDUoYSkuIAoKUmVwb3J0IGl0IGluIHRoZSB0YWJsZSBhbmQgY29tbWVudCBicmllZmx5LgoKCmBgYHtyfQojIGFuYWx5c2lzCmRmX2x1eCRPdXRjb21lIDwtIGRmX2x1eF9hbGwkT3V0Y29tZQpkZl9sdXgKCmRmX2x1eDIkT3V0Y29tZSA8LSBkZl9sdXhfYWxsJE91dGNvbWVbLWRpc2NhcmRlZF0KZGZfbHV4MgoKbmV5bWFuIDwtIGZ1bmN0aW9uKG91dGNvbWUsIHRyZWF0LCBhbHBoYSl7CiAgCiAgIyBTZXR0aW5ncwogIFl0IDwtIG91dGNvbWVbdHJlYXQgPT0gMV0KICBZYyA8LSBvdXRjb21lW3RyZWF0ID09IDBdCiAgTnQgPC0gbGVuZ3RoKFl0KQogIE5jIDwtIGxlbmd0aChZYykKICAKICAjIEFURQogIGF0ZSA8LSBtZWFuKFl0KSAtIG1lYW4oWWMpCiAgCiAgIyBWYXJpYW5jZSBlc3RpbWF0b3IKICBWYXIudCA8LSB2YXIoWXQpCiAgVmFyLmMgPC0gdmFyKFljKQogIFZhciA8LSBWYXIudCAvIE50ICsgVmFyLmMgLyBOYwogIAogICMgRXhwb3J0aW5nIHJlc3VsdHMKICByZXMgPC0gY2JpbmQoQVRFID0gYXRlLCBWYXIgPSBWYXIsIGludC5sb3dlciA9IGF0ZSAtIHNxcnQoVmFyKSpxbm9ybSgxLWFscGhhLzIpLCBpbnQudXBwZXIgPSBhdGUgKyBzcXJ0KFZhcikqcW5vcm0oMS1hbHBoYS8yKSkKICByZXR1cm4ocmVzKQp9CmBgYAoKCmBgYHtyfQojIyMgMikgQVRFLCBOYWl2ZSBOZXltYW4gKGkuZS4sIE5leW1hbiBhcyBpZiBkYXRhc2V0IHdhcyByYW5kb21pemVkKQpuZXkubmFpdmUgPC0gbmV5bWFuKG91dGNvbWUgPSBkZl9sdXgkT3V0Y29tZSwgdHJlYXQgPSBkZl9sdXgkVFJFQVQsIGFscGhhID0gMC4wNSkKbmV5Lm5haXZlCgojIyMgMykgQVRFLCBOZXltYW4gb24gdGhlIHRyaW1tZWQgc3Vic2V0Cm5leS50cmltbSA8LSBuZXltYW4ob3V0Y29tZSA9IGRmX2x1eDIkT3V0Y29tZSwgdHJlYXQgPSBkZl9sdXgyJFRSRUFULCBhbHBoYSA9IDAuMDUpCm5leS50cmltbQoKbmV5IDwtIHJiaW5kKG5leS5uYWl2ZSxuZXkudHJpbW0pCgpyb3duYW1lcyhuZXkpIDwtIGMoIkFURSBOYWl2ZSBOZXltYW4iLCAiQVRFIE5haXZlIE5leW1hbiBvbiB0cmltbWVkIHN1YnNldCIpCnJvdW5kKG5leSwyKQpgYGAKVGhlIGludGVydmFsIG9uIHRoZSB0cmltbWVkIHN1YnNldCBpcyBzbGlnaHRseSBuYXJyb3dlci4KCgojIyA3LiBZb3VyIG93biBwcmVmZXJyZWQgYW5hbHlzaXMuIFVzZSBhbnkgb3RoZXIgbWV0aG9kcyAoTWF0Y2hpbmcgd2l0aCBvciB3aXRob3V0IHJlcGxhY2VtZW50LCBCaWFzLWFkanVzdGVkIGVzdGltYXRvcnMsIElQVFcsIHJlZ3Jlc3Npb24sIERSLCBDQVJULCBCQVJUIGV0Yy4uLikgZm9yIGVzdGltYXRpbmcgQVRFLCBBVFQgb3Igb3RoZXIgY2F1c2FsIGVmZmVjdHMgeW91IGJlbGlldmUgYXJlIHJlbGV2YW50IGZvciB0aGUgc3R1ZHkuIFlvdSBtYXkgYWxzbyBpbnZlc3RpZ2F0ZSB0cmVhdG1lbnQgZWZmZWN0IGhldGVyb2dlbmVpdHkgYW5kIGVzdGltYXRlIENBVEVzLiBJbiBSIHlvdSBjYW4gdXNlIGFueSBwYWNrYWdlLCBpbmNsdWRpbmcgdGhlIG9uZXMgc3VnZ2VzdGVkIGR1cmluZyBsZWN0dXJlcywgZS5nLiwgTWF0Y2hpdCkuIAoKUGxlYXNlIGJyaWVmbHkgcHJlc2VudCB0aGUgbWV0aG9kIGFuZCBleHBsYWluIHlvdXIgcHJlZmVyZW5jZSBhbmQgZGlzY3VzcyByZXN1bHRzIHlvdSBoYXZlIG9idGFpbmVkLgoKYGBge3J9CiMjIyA1KSBFc3RpbWF0aW5nIEFUVCBvbiBtYXRjaGVkIGRhdGEKCiMgNC4yKSBOb24tRXhhY3QgTWF0Y2hpbmc6IE5lYXJlc3QgTmVpZ2hib3JzIHdpdGggUHJvcGVuc2l0eSBTY29yZQogCiMgV2l0aG91dCByZXBsYWNlbWVudCwgZGlzY2FyZGluZyBjb250cm9sIHVuaXRzICAKIyBvdXRzaWRlIHRoZSBzdXBwb3J0IG9mIHRoZSBkaXN0YW5jZSBtZWFzdXJlIG9mIHRoZSB0cmVhdGVkIHVuaXRzIAptLm5uMSA8LSBtYXRjaGl0KFRSRUFUfiAuLCBkYXRhID0gZGZfbHV4LCBtZXRob2QgPSAibmVhcmVzdCIsIGRpc2NhcmQ9J2NvbnRyb2wnLCBkaXN0YW5jZSA9ICJnbG0iKQptLm5uMQpzdW1tYXJ5KG0ubm4xKSAjIyBob3cgbWFueSB1bml0cyBhcmUgbWF0Y2hlZD8gLS0+ICJUSEUiIG5lYXJlc3QgbmVpZ2hib3IKcGxvdChzdW1tYXJ5KG0ubm4xKSkKc3VtbWFyeShtLm5uMSRkaXN0YW5jZSkKc3VtbWFyeShwc2NvcmVzMikKCiMgT2J0YWluIG1hdGNoZWQgZGF0YXNldCBmcm9tIE1hdGNoSXQgb3V0cHV0Cm0ubXlkYXRhIDwtIG1hdGNoLmRhdGEobS5ubjEpCmhlYWQobS5teWRhdGEpCgojIE5leW1hbidzIG1ldGhvZCBmb3IgZXN0aW1hdGluZyBjYXVzYWwgZWZmZWN0cyBpcyB1c2VkIHRvIGNhbGN1bGF0ZSB0aGUgQXZlcmFnZSBUcmVhdG1lbnQgRWZmZWN0IG9uIHRoZSBUcmVhdGVkIChBVFQpCm5leV9tYXRjaCA8LSBuZXltYW4ob3V0Y29tZSA9IG0ubXlkYXRhJE91dGNvbWUsIHRyZWF0ID0gbS5teWRhdGEkVFJFQVQsIGFscGhhID0gMC4wNSkKbmV5X21hdGNoCgojIFJlZ3Jlc3Npb24Kc3VtbWFyeShsbShPdXRjb21lflRSRUFULCBkYXRhPW0ubXlkYXRhKSkKYGBgCkkndmUgdXNlZCBNYXRjaEl0IGZ1bmN0aW9uIHdpdGggdGhlICJuZWFyZXN0IiBtZXRob2QgdG8gcGVyZm9ybSBuZWFyZXN0IG5laWdoYm9yIG1hdGNoaW5nIHdpdGhvdXQgcmVwbGFjZW1lbnQgYW5kIE5leW1hbidzIG1ldGhvZCB0byBlc3RpbWF0ZSB0aGUgQVRUIHVzaW5nIHRoZSBtYXRjaGVkIGRhdGFzZXQuIApUaGUgYmFsYW5jZSBhc3Nlc3NtZW50IHNob3dzIHRoYXQgdGhlIG1hdGNoaW5nIHByb2NlZHVyZSBoYXMgaW1wcm92ZWQgYmFsYW5jZSBhY3Jvc3MgY292YXJpYXRlcyBpbiB0aGUgbWF0Y2hlZCBkYXRhc2V0LgoKVGhlIGVzdGltYXRlZCBBVFQgaXMgYXBwcm94aW1hdGVseSAwLjAzMDQgd2l0aCBhIGNvbmZpZGVuY2UgaW50ZXJ2YWwgc3Bhbm5pbmcgZnJvbSAtMC4wMTE1IHRvIDAuMDcyMywgdGh1cyB0aGUgdHJlYXRtZW50IGVmZmVjdCBtYXkgYmUgcG9zaXRpdmUsIG5lZ2F0aXZlIG9yIHplcm8uCgpUaGUgbGluZWFyIHJlZ3Jlc3Npb24gcmVzdWx0cyBjb25maXJtIHRoZSBlc3RpbWF0ZWQgdHJlYXRtZW50IGVmZmVjdCwgYnV0IHdpdGggdGhlIGFic2VuY2Ugb2Ygc3RhdGlzdGljYWwgc2lnbmlmaWNhbmNlIGluIHRoZSByZWdyZXNzaW9uIHJlc3VsdHMgc3VnZ2VzdHMgdGhhdCB0aGVyZSBpc24ndCBzdHJvbmcgZXZpZGVuY2UgdG8gcmVqZWN0IHRoZSBudWxsIGh5cG90aGVzaXMgdGhhdCB0aGUgdHJlYXRtZW50IGVmZmVjdCBpcyB6ZXJvLgoK